mod cache; mod database; mod error; mod store; mod table; use async_std::{ channel::{unbounded, Sender}, path::PathBuf, task::spawn, }; use cache::Cache; use database::Database; use error::{ErrorCode, MTTError}; use store::Store; use table::Table; const ENTRY: &str = "EntryPoint"; #[derive(Debug)] pub struct ToCacheMsg { data: D, result: Sender, } #[derive(Debug)] pub enum ToCache { Get(ToCacheMsg), Commit(ToCacheMsg), } #[derive(Clone, Debug)] pub enum FromCache { Ok, Str(Store), DB(Database), Error(MTTError), } #[derive(Clone, Debug)] pub struct Data { id: Option, data: Option, } impl Data { fn from_id(id: S) -> Self where S: Into, { Self { id: Some(id.into()), data: None, } } fn from_data(data: D) -> Self { Self { id: None, data: Some(data), } } } #[derive(Clone)] pub struct MoreThanText { to_cache: Sender, entry: Data, } impl MoreThanText { fn new(to_cache: Sender) -> Self { Self { to_cache: to_cache, entry: Data::from_id(ENTRY), } } async fn session(&self) -> Result { let (s, r) = unbounded(); let msg = ToCacheMsg { data: ENTRY.to_string(), result: s, }; self.to_cache.send(ToCache::Get(msg)).await.unwrap(); match r.recv().await.unwrap() { FromCache::Str(store) => Ok(store), FromCache::Error(err) => Err(err), _ => unreachable!(), } } async fn commit(&self, store: Store) -> Result<(), MTTError> { let (s, r) = unbounded(); let msg = ToCacheMsg { data: store, result: s, }; self.to_cache.send(ToCache::Commit(msg)).await.unwrap(); match r.recv().await.unwrap() { FromCache::Ok => Ok(()), FromCache::Error(err) => Err(err), _ => unreachable!(), } } } #[cfg(test)] mod mtt { use super::*; use tempfile::tempdir; #[async_std::test] async fn create_new() { let dir = tempdir().unwrap(); let mtt = start_db(dir.path()).await.unwrap(); assert_eq!(mtt.entry.id, Some(ENTRY.to_string())); assert!(mtt.entry.data.is_none()); let store = mtt.session().await.unwrap(); let expected: Vec = Vec::new(); assert_eq!(store.list(), expected); } #[async_std::test] async fn commit_db() { let dir = tempdir().unwrap(); let db = "fred"; let mtt = start_db(dir.path()).await.unwrap(); let mut store = mtt.session().await.unwrap(); store.add(db).unwrap(); mtt.commit(store).await.unwrap(); let store2 = mtt.session().await.unwrap(); assert_eq!(store2.list(), [db]); } #[async_std::test] async fn commit_from_multiple_sources() { let dir = tempdir().unwrap(); let mtt1 = start_db(dir.path()).await.unwrap(); let mtt2 = mtt1.clone(); let db1 = "first"; let db2 = "second"; let mut store1 = mtt1.session().await.unwrap(); let mut store2 = mtt2.session().await.unwrap(); store1.add(db1).unwrap(); store2.add(db2).unwrap(); mtt1.commit(store1).await.unwrap(); mtt2.commit(store2).await.unwrap(); let output = mtt1.session().await.unwrap(); assert_eq!(output.list(), [db1, db2]); } #[async_std::test] async fn fail_on_duplicates() { let dir = tempdir().unwrap(); let mtt1 = start_db(dir.path()).await.unwrap(); let mtt2 = mtt1.clone(); let name = "unique_only"; let mut store1 = mtt1.session().await.unwrap(); let mut store2 = mtt2.session().await.unwrap(); store1.add(name).unwrap(); store2.add(name).unwrap(); mtt1.commit(store1).await.unwrap(); let output = mtt2.commit(store2).await; match output { Ok(_) => assert!(false, "Should have returned an error"), Err(err) => match err.code { ErrorCode::DuplicateDatabase(_) => (), _ => assert!(false, "{:?} is not ErrorCode::DuplicateDatabase", err.code), }, } } } pub async fn start_db

(dir: P) -> Result where P: Into, { let path = dir.into(); let (s, r) = unbounded(); spawn(async move { let mut cache = Cache::new(path).await; cache.listen(r).await; }); Ok(MoreThanText::new(s)) }