use super::{DBError, ErrorCode, FileData, SessionData}; use std::{collections::HashMap, slice, str}; #[derive(Clone)] pub struct Store { db_map: HashMap, } 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 for Store { fn to_bytes(&self) -> Vec { 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) -> Result { let mut output = Store::new(); let mut name: Vec = Vec::new(); let mut id: Vec = 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, 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, 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, DBError> { for key in keys { match Self::test_key(key) { Ok(_) => (), Err(err) => return Err(err), } } let mut names: Vec = 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 = 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 = 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::::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); } }