clippable/clippable-svc/src/db.rs

118 lines
3.4 KiB
Rust

#[cfg(feature = "admin")]
// This module defines a tiny async interface for the "database" that this
// project uses for interfacing with the key store
// WARN: at the moment there are no guarantees as far as data integrity is
// concerned. This means there are no real transactions
use std::env;
use std::fs::OpenOptions;
use std::io::{BufWriter, BufReader};
use std::path::PathBuf;
use std::collections::HashMap;
use rocket::serde::{Serialize, Deserialize};
lazy_static! {
pub static ref DB_PATH: String = {
env::var("DB_PATH").unwrap_or("keys.db".into())
};
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Database {
// uid's while random are fine to release as public as the key is more
// important however ideally neither should be release. Furthermore
// the frontend assists in keeping these secret by treating both as
// password fields as they are both randomly generated via a script
// uid -> key
users: HashMap<String,String>,
#[serde(skip)]
path: PathBuf
}
impl Database {
// Opens a handle to a database file
// if none is found then one is created with the new path
// if there is one then the existing database is used
// any thing else is invalid and causes this to return Err
pub fn new(path: PathBuf) -> Result<Self, std::io::Error> {
let file = OpenOptions::new()
.write(true)
.create(true)
.open(&path)?;
let writer = BufWriter::new(&file);
// Dummy value to write in place
let empty = Database { users: HashMap::new(), path: "".into() };
serde_json::to_writer(writer, &empty)?;
Ok(empty)
}
pub fn load(path: PathBuf) -> Result<Self, std::io::Error> {
let file = OpenOptions::new()
.read(true)
.open(&path)?;
let reader = BufReader::new(&file);
let mut data: Database = serde_json::from_reader(reader)?;
data.path = path;
return Ok(data);
}
pub fn get(&self, uid: &str) -> Option<&String> {
return self.users.get(uid);
}
fn write(&self) -> Result<(), std::io::Error> {
let file = OpenOptions::new()
.write(true)
.open(&self.path)?;
let writer = BufWriter::new(file);
serde_json::to_writer(writer, &self.path)?;
return Ok(())
}
pub fn remove(&mut self, uid: &str) -> Result<(), std::io::Error> {
self.users.remove_entry(uid);
self.write()
}
pub fn add(&mut self, key: &str, value: &str) -> Result<(), std::io::Error> {
println!("{:?}", self.path);
self.users.insert(key.into(), value.into());
self.write()
}
}
#[cfg(test)]
mod db_tests {
use super::Database;
const DB: &'static str = "new.db";
#[test]
fn load_db() {
match Database::new(DB.into()) {
Ok(db) => println!("Loaded new sample.db: {:?}", db),
Err(e) => panic!("Error fetching database: {}", e)
}
}
#[test]
fn add_simple_entries() {
match Database::load(DB.into()) {
Ok(mut db) => db.add("key", "value").unwrap(),
Err(e) => println!("Error adding entries: {}", e)
}
}
#[test]
fn remove_simple_entries() {
match Database::load(DB.into()) {
Ok(mut db) => db.remove("key").unwrap(),
Err(e) => println!("Error removing simple entries: {}", e)
}
}
}