mod database; mod entry; mod error; mod session; mod store; use async_std::path::PathBuf; use database::Database; use entry::Entry; use error::{DBError, ErrorCode}; use session::Session; use std::{slice, str}; use store::Store; const ENTRY: &str = "EntryPoint"; trait FileData { fn to_bytes(&self) -> Vec; fn from_bytes(data: &mut slice::Iter) -> Result; } trait SessionData { fn add(&mut self, key: &str, value: &str, data: &str) -> Result, DBError>; fn eq(&self, key: &str, value: &str) -> Result, DBError>; fn list(&self, keys: Vec<&str>) -> Result, DBError>; } #[derive(Clone)] pub enum DataType { DBMap(Store), TableMap(Database), } impl DataType { fn new(data_type: &str) -> Result { match data_type { "store" => Ok(DataType::DBMap(Store::new())), "database" => Ok(DataType::TableMap(Database::new())), _ => Err(DBError::from_code(ErrorCode::DataTypeIncorrect( data_type.to_string(), ))), } } } impl SessionData for DataType { fn add(&mut self, key: &str, value: &str, data: &str) -> Result, DBError> { match self { DataType::DBMap(dbs) => dbs.add(key, value, data), DataType::TableMap(_) => todo!(), } } fn eq(&self, key: &str, value: &str) -> Result, DBError> { match self { DataType::DBMap(dbs) => dbs.eq(key, value), DataType::TableMap(_) => todo!(), } } fn list(&self, keys: Vec<&str>) -> Result, DBError> { match self { DataType::DBMap(dbs) => dbs.list(keys), DataType::TableMap(_) => todo!(), } } } impl FileData for DataType { fn to_bytes(&self) -> Vec { let mut output = Vec::new(); match self { DataType::DBMap(_) => output.append(&mut "DBMap".as_bytes().to_vec()), DataType::TableMap(_) => output.append(&mut "TableMap".as_bytes().to_vec()), } output.push(0); match self { DataType::DBMap(store) => output.append(&mut store.to_bytes()), DataType::TableMap(_) => (), } output } fn from_bytes(data: &mut slice::Iter) -> Result { let mut header: Vec = Vec::new(); loop { let letter = match data.next() { Some(a) => a.clone(), None => 0, }; if letter == 0 { break; } else { header.push(letter); } } let header = match str::from_utf8(&header) { Ok(item) => item, Err(_) => return Err(DBError::from_code(ErrorCode::CorruptFile)), }; match header { "DBMap" => match Store::from_bytes(data) { Ok(store) => Ok(DataType::DBMap(store)), Err(err) => Err(err), }, "TableMap" => Ok(DataType::new("database").unwrap()), _ => Err(DBError::from_code(ErrorCode::CorruptFile)), } } } #[derive(Clone)] pub struct MoreThanText; impl MoreThanText { pub async fn new

(dir: P) -> Result where P: Into, { let pathbuf = dir.into(); let entry = pathbuf.as_path().join(ENTRY); match Entry::get(entry.clone()).await { Ok(_) => Ok(Self {}), Err(_) => { let store = DataType::new("store").unwrap(); match Entry::new(entry, store).await { Ok(_) => Ok(Self {}), Err(err) => { let mut error = DBError::from_code(ErrorCode::CacheReadWrite); error.add_source(err); Err(error) } } } } } fn new_session(&self) -> Session { Session::new() } } #[cfg(test)] mod datatype { use super::*; #[test] fn bad_data_type() -> Result<(), DBError> { let dt = "bufcuss"; match DataType::new(dt) { Ok(_) => Err(DBError::new("should have produced an error")), Err(err) => match err.code { ErrorCode::DataTypeIncorrect(value) => { assert_eq!(value, dt, "Incorrect input value"); Ok(()) } _ => { let mut error = DBError::new("incorrect error"); error.add_source(err); Err(error) } }, } } #[test] fn create_store() -> Result<(), DBError> { match DataType::new("store") { Ok(dt) => match dt { DataType::DBMap(_) => Ok(()), _ => Err(DBError::new("incorrect data type")), }, Err(err) => Err(err), } } #[test] fn create_database() -> Result<(), DBError> { match DataType::new("database") { Ok(dt) => match dt { DataType::TableMap(_) => Ok(()), _ => Err(DBError::new("incorrect data type")), }, Err(err) => Err(err), } } } #[cfg(test)] mod datatype_sesssion { use super::*; #[test] fn update_storage() { let mut dbs = DataType::new("store").unwrap(); let name = "new_database"; let id = "someid"; dbs.add("database", name, id).unwrap(); assert_eq!(dbs.eq("database", name).unwrap(), [id].to_vec()); assert_eq!(dbs.list(["database"].to_vec()).unwrap(), [name].to_vec()); } } #[cfg(test)] mod datatype_file { use super::*; #[test] fn new_store_bytes() { let dbs = DataType::new("store").unwrap(); let mut expected = "DBMap".as_bytes().to_vec(); expected.push(0); assert_eq!(dbs.to_bytes(), expected); } #[test] fn store_bytes_with_info() { let name = "title"; let id = "king"; let mut store = Store::new(); let mut dt_store = DataType::new("store").unwrap(); let mut expected = dt_store.to_bytes(); store.add("database", name, id).unwrap(); expected.append(&mut store.to_bytes()); dt_store.add("database", name, id).unwrap(); assert_eq!(dt_store.to_bytes(), expected); } #[test] fn read_empty_store() { let dt_store = DataType::new("store").unwrap(); let data = dt_store.to_bytes(); let mut feed = data.iter(); let output = DataType::from_bytes(&mut feed).unwrap(); assert_eq!( dt_store.list(["database"].to_vec()).unwrap(), output.list(["database"].to_vec()).unwrap() ); } #[test] fn read_store_info() { let mut dt_store = DataType::new("store").unwrap(); dt_store.add("database", "raven", "beastboy").unwrap(); let data = dt_store.to_bytes(); let mut feed = data.iter(); let output = DataType::from_bytes(&mut feed).unwrap(); assert_eq!( dt_store.list(["database"].to_vec()).unwrap(), output.list(["database"].to_vec()).unwrap() ); } #[test] fn new_database_bytes() { let db = DataType::new("database").unwrap(); let mut expected = "TableMap".as_bytes().to_vec(); expected.push(0); assert_eq!(db.to_bytes(), expected); } #[test] fn read_empty_database() { let dt = DataType::new("database").unwrap(); let data = dt.to_bytes(); let mut feed = data.iter(); match DataType::from_bytes(&mut feed).unwrap() { DataType::TableMap(_) => (), _ => assert!(false, "Incorrect data type"), } } #[test] fn read_bad_header() -> Result<(), DBError> { let data = "sdghsdl".as_bytes().to_vec(); let mut feed = data.iter(); match DataType::from_bytes(&mut feed) { Ok(_) => Err(DBError::new("should have raised an error")), Err(err) => match err.code { ErrorCode::CorruptFile => Ok(()), _ => Err(DBError::new("incorrect error")), }, } } #[test] fn read_bad_store() -> Result<(), DBError> { let mut data = "DBMap".as_bytes().to_vec(); data.push(0); data.append(&mut "sdfgs".as_bytes().to_vec()); let mut feed = data.iter(); match DataType::from_bytes(&mut feed) { Ok(_) => Err(DBError::new("should have raised an error")), Err(err) => match err.code { ErrorCode::CorruptFile => Ok(()), _ => Err(DBError::new("incorrect error code")), }, } } } #[cfg(test)] mod create { use super::*; use async_std::fs::write; use std::error::Error; use tempfile::tempdir; #[async_std::test] async fn create() { let dir = tempdir().unwrap(); MoreThanText::new(dir.path().to_str().unwrap()) .await .unwrap(); let epoint = dir.path().join(ENTRY); assert!( epoint.is_file(), "{} did not get created.", epoint.display() ); let entry = Entry::get(epoint.to_str().unwrap()).await.unwrap(); assert_eq!( entry.data().list(["database"].to_vec()).unwrap(), Vec::::new() ); } #[async_std::test] async fn entry_failure() -> Result<(), DBError> { let dir = tempdir().unwrap(); let path = dir.path().join("bad").join("path"); match MoreThanText::new(path).await { Ok(_) => Err(DBError::new("Should have produced an error.")), Err(err) => match err.code { ErrorCode::CacheReadWrite => { assert!(err.source().is_some(), "Error should have a source."); assert!( err.source().unwrap().to_string().contains("write failure"), "Source Error Message: {}", err.source().unwrap().to_string() ); Ok(()) } _ => Err(DBError::new("incorrect error code")), }, } } #[async_std::test] async fn existing_entry_point() { let dir = tempdir().unwrap(); let data = DataType::new("store").unwrap(); Entry::new(dir.path().join(ENTRY), data.clone()) .await .unwrap(); MoreThanText::new(dir.path()).await.unwrap(); } #[async_std::test] async fn corrupt_enty_point() -> Result<(), DBError> { let dir = tempdir().unwrap(); let file = dir.path().join(ENTRY); write(file, b"Really bad data.").await.unwrap(); match MoreThanText::new(dir.path()).await { Ok(_) => Err(DBError::new("should have errored")), Err(_) => Ok(()), } } } #[cfg(test)] mod sessions { use super::*; use tempfile::tempdir; #[async_std::test] async fn create_session() { let dir = tempdir().unwrap(); let mtt = MoreThanText::new(dir.path().to_str().unwrap()).await.unwrap(); mtt.new_session(); } }