clippable/api/src/db.rs
shockrah ff73995ff3 !!+ Optional backend code for admin feature
Features implemented thus far are proven to be working however
they will be left as optional features until they are more properly
tested in some kind of staging environment
2022-02-02 23:02:39 -08:00

114 lines
3.3 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::fs::OpenOptions;
use std::io::prelude::Write;
use std::io::{BufWriter, BufReader};
use std::path::PathBuf;
use std::collections::HashMap;
use rocket::serde::{Serialize, Deserialize};
#[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;
use rocket::tokio;
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)
}
}
}