#[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, #[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 { 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 { 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) } } }