morethantext-web/src/morethantext/store.rs

307 lines
9.3 KiB
Rust

use super::{DBError, ErrorCode, FileData, SessionData};
use std::{collections::HashMap, slice, str};
#[derive(Clone)]
pub struct Store {
db_map: HashMap<String, String>,
}
impl Store {
pub fn new() -> Self {
Self {
db_map: HashMap::new(),
}
}
fn test_key(key: &str) -> Result<(), DBError> {
match key {
"database" => (),
_ => return Err(DBError::new(format!("databases do not have a {}", key))),
}
Ok(())
}
}
impl FileData<Self> for Store {
fn to_bytes(&self) -> Vec<u8> {
let mut output = Vec::new();
for (name, id) in self.db_map.iter() {
output.append(&mut name.as_bytes().to_vec());
output.push(0);
output.append(&mut id.as_bytes().to_vec());
output.push(0);
}
output
}
fn from_bytes(data: &mut slice::Iter<u8>) -> Result<Self, DBError> {
let mut output = Store::new();
let mut name: Vec<u8> = Vec::new();
let mut id: Vec<u8> = Vec::new();
let mut get_id = false;
let mut letter: u8;
loop {
match data.next() {
Some(a) => letter = a.clone(),
None => {
if !name.is_empty() {
return Err(DBError::from_code(ErrorCode::CorruptFile));
}
break;
}
}
if letter == 0 {
if get_id {
let name_holder = match str::from_utf8(&name) {
Ok(item) => item,
Err(_) => return Err(DBError::from_code(ErrorCode::CorruptFile)),
};
let id_holder = match str::from_utf8(&id) {
Ok(item) => item,
Err(_) => return Err(DBError::from_code(ErrorCode::CorruptFile)),
};
match output.add("database", name_holder, id_holder) {
Ok(_) => (),
Err(err) => {
let mut error = DBError::from_code(ErrorCode::CorruptFile);
error.add_source(err);
return Err(error);
}
};
name.clear();
id.clear();
}
get_id = !get_id;
} else {
if get_id {
id.push(letter);
} else {
name.push(letter);
}
}
}
Ok(output)
}
}
impl SessionData for Store {
fn add(&mut self, key: &str, value: &str, data: &str) -> Result<Vec<String>, DBError> {
match Self::test_key(key) {
Ok(_) => (),
Err(err) => return Err(err),
}
match self.db_map.get(value) {
Some(_) => return Err(DBError::new(format!("database {} already exists", value))),
None => (),
}
self.db_map.insert(value.to_string(), data.to_string());
let mut output = Vec::new();
output.push(data.to_string());
Ok(output)
}
fn eq(&self, key: &str, value: &str) -> Result<Vec<String>, DBError> {
match Self::test_key(key) {
Ok(_) => (),
Err(err) => return Err(err),
}
let mut output = Vec::new();
match self.db_map.get(value) {
Some(data) => output.push(data.to_string()),
None => (),
}
Ok(output)
}
fn list(&self, keys: Vec<&str>) -> Result<Vec<String>, DBError> {
for key in keys {
match Self::test_key(key) {
Ok(_) => (),
Err(err) => return Err(err),
}
}
let mut names: Vec<String> = self.db_map.clone().into_keys().collect();
names.sort();
Ok(names)
}
}
#[cfg(test)]
mod file_data {
use super::*;
use std::error::Error;
#[test]
fn to_bytes_new() {
let dbs = Store::new();
let expected: Vec<u8> = Vec::new();
let output = dbs.to_bytes();
assert_eq!(output, expected);
}
#[test]
fn to_bytes_with_database() {
let mut dbs = Store::new();
let name = "something";
let id = "id";
dbs.add("database", name, id).unwrap();
let mut expected: Vec<u8> = Vec::new();
expected.append(&mut name.as_bytes().to_vec());
expected.push(0);
expected.append(&mut id.as_bytes().to_vec());
expected.push(0);
let output = dbs.to_bytes();
assert_eq!(output, expected);
}
#[test]
fn from_bytes() {
let mut dbs = Store::new();
dbs.add("database", "one", "1").unwrap();
dbs.add("database", "two", "2").unwrap();
dbs.add("database", "three", "3").unwrap();
let data = dbs.to_bytes();
let mut feed = data.iter();
let output = Store::from_bytes(&mut feed).unwrap();
assert_eq!(output.db_map, dbs.db_map);
}
#[test]
fn from_bytes_incomplete_name() -> Result<(), DBError> {
let data = "notName".as_bytes();
let mut feed = data.iter();
match Store::from_bytes(&mut feed) {
Ok(_) => Err(DBError::new("should have produced an errpr")),
Err(err) => match err.code {
ErrorCode::CorruptFile => Ok(()),
_ => Err(DBError::new("incorrect error code")),
},
}
}
#[test]
fn from_bytes_incomplete_id() -> Result<(), DBError> {
let mut data = "proper".as_bytes().to_vec();
data.push(0);
data.append(&mut "nope".as_bytes().to_vec());
let mut feed = data.iter();
match Store::from_bytes(&mut feed) {
Ok(_) => Err(DBError::new("should have produced an error")),
Err(err) => match err.code {
ErrorCode::CorruptFile => Ok(()),
_ => Err(DBError::new("incorrect error code")),
},
}
}
#[test]
fn from_bytes_handles_error() -> Result<(), DBError> {
let name = "duplicate";
let mut data = name.as_bytes().to_vec();
data.push(0);
data.append(&mut "first".as_bytes().to_vec());
data.push(0);
data.append(&mut "duplicate".as_bytes().to_vec());
data.push(0);
data.append(&mut "second".as_bytes().to_vec());
data.push(0);
let mut feed = data.iter();
match Store::from_bytes(&mut feed) {
Ok(_) => Err(DBError::new("should have returned an error")),
Err(err) => match err.code {
ErrorCode::CorruptFile => {
assert!(
err.source().is_some(),
"Should state file corruption cause."
);
assert_eq!(
err.source().unwrap().to_string(),
format!("database {} already exists", name)
);
Ok(())
}
_ => Err(DBError::new("incorrect error code")),
},
}
}
}
#[cfg(test)]
mod session_data {
use super::*;
#[test]
fn add_new() {
let mut dbs = Store::new();
let key = "database";
let value = "marvin";
let data = "123456";
assert_eq!(dbs.add(key, value, data).unwrap(), [data]);
let output = dbs.eq(key, value).unwrap();
assert_eq!(output, [data]);
}
#[test]
fn add_bad_key() {
let mut dbs = Store::new();
let key = "sdgfjksg";
match dbs.add(key, "fred", "barney") {
Ok(_) => assert!(false, "Bad keys should produce an error."),
Err(err) => assert_eq!(err.to_string(), format!("databases do not have a {}", key)),
}
}
#[test]
fn get_bad_key() {
let dbs = Store::new();
let key = "bvdfgert";
match dbs.eq(key, "fred") {
Ok(_) => assert!(false, "Bad keys should produce an error."),
Err(_) => (),
}
}
#[test]
fn unique_names() {
let mut dbs = Store::new();
let value = "wilma";
dbs.add("database", value, "something").unwrap();
match dbs.add("database", value, "overwrite") {
Ok(_) => assert!(false, "Duplicate names should produce an error."),
Err(err) => assert_eq!(
err.to_string(),
format!("database {} already exists", value)
),
}
}
#[test]
fn get_missing() {
let dbs = Store::new();
let output = dbs.eq("database", "melvin").unwrap();
assert_eq!(output, Vec::<String>::new());
}
#[test]
fn list_bad_keys() {
let dbs = Store::new();
let key = "sdfgren";
let keys = [key];
match dbs.list(keys.to_vec()) {
Ok(_) => assert!(false, "Bad keys should error."),
Err(err) => assert_eq!(err.to_string(), format!("databases do not have a {}", key)),
}
}
#[test]
fn list_is_sorted() {
let mut dbs = Store::new();
let mut data = ["fred", "barney", "wilma", "betty", "pebbles", "bambam"];
for db in data {
dbs.add("database", db, db).unwrap();
}
data.sort();
assert_eq!(dbs.list(["database"].to_vec()).unwrap(), data);
}
}