use async_std::{ channel::{unbounded, Receiver, Sender}, path::PathBuf, task::spawn, }; use std::{collections::HashMap, error::Error, fmt}; const ENTRY: &str = "EntryPoint"; #[derive(Debug)] enum ErrorCode { Undefined(String), IncorrectDataType(String), } impl fmt::Display for ErrorCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ErrorCode::Undefined(msg) => write!(f, "{}", msg), ErrorCode::IncorrectDataType(item) => write!(f, "data type '{}' does not exist", item), } } } mod errorcodes { use super::*; const ITEMS: [&str; 2] = ["one", "two"]; #[test] fn undefined_display() { for item in ITEMS { let err = ErrorCode::Undefined(item.to_string()); assert_eq!(err.to_string(), item); } } #[test] fn incorrect_data_type() { for item in ITEMS { let err = ErrorCode::IncorrectDataType(item.to_string()); assert_eq!( err.to_string(), format!("data type '{}' does not exist", item) ); } } } #[derive(Debug)] pub struct MTTError { code: ErrorCode, } impl MTTError { fn new(msg: S) -> Self where S: Into, { let text = msg.into(); Self { code: ErrorCode::Undefined(text), } } fn from_code(code: ErrorCode) -> Self { Self { code: code } } } impl Error for MTTError {} impl fmt::Display for MTTError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.code) } } #[cfg(test)] mod errors { use super::*; #[test] fn create_with_str() { let msgs = ["one", "two"]; for msg in msgs { let err = MTTError::new(msg); assert_eq!(err.to_string(), msg); } } #[test] fn create_with_string() { let msg = "three"; let err = MTTError::new(msg.to_string()); assert_eq!(err.to_string(), msg); } #[test] fn create_from_code() { let code = ErrorCode::Undefined("oops".to_string()); let err = MTTError::from_code(code); match err.code { ErrorCode::Undefined(_) => (), _ => assert!(false, "{:?} is not undefined", err.code), } } #[test] fn create_from_different_code() { let code = ErrorCode::IncorrectDataType("nuts".to_string()); let err = MTTError::from_code(code); match err.code { ErrorCode::IncorrectDataType(_) => (), _ => assert!(false, "{:?} is not incorrect data type", err.code), } } } #[derive(Clone, Debug)] struct Store; impl Store { fn new() -> Self { Self {} } } #[cfg(test)] mod stores { use super::*; #[test] fn create() { Store::new(); } } #[derive(Clone, Debug)] enum DataType { DBMap(Store), } impl DataType { fn new(dtype: &str) -> Result { match dtype { "store" => Ok(Self::DBMap(Store::new())), _ => Err(MTTError::from_code(ErrorCode::IncorrectDataType( dtype.to_string(), ))), } } } #[cfg(test)] mod datatypes { use super::*; #[test] fn error_on_bad_datatype() { let items = ["sdgthsth", "jfjty"]; for item in items { match DataType::new(item) { Ok(_) => assert!(false, "bad data types should return an error"), Err(err) => match err.code { ErrorCode::IncorrectDataType(dtype) => assert_eq!(dtype, item), _ => assert!(false, "{:?} is not incorrect data type", err.code), }, } } } #[test] fn create_store() { let dtype = DataType::new("store").unwrap(); match dtype { DataType::DBMap(_) => (), _ => assert!(false, "{:?} is not incorrect data type", dtype), } } } #[derive(Debug)] enum FromCache { Data(HashMap), Error(MTTError), } struct CacheQuery { ids: Vec, reply: Sender, } enum ToCache { Query(CacheQuery), } #[derive(Clone)] pub struct MoreThanText { session: Vec, cache: Sender>, } impl MoreThanText { async fn new(cache: Sender>) -> Result { Ok(Self { session: [ENTRY.to_string()].to_vec(), cache: cache, }) } } #[cfg(test)] mod mtt { use super::*; #[async_std::test] async fn create() { let (s, _) = unbounded(); let mtt = MoreThanText::new(s).await.unwrap(); assert_eq!(mtt.session, [ENTRY]); } } struct Cache { channel: Receiver, } impl Cache { async fn new

(_dir: P, channel: Receiver) -> Result where P: Into, { Ok(Self { channel: channel }) } async fn start(&self) { loop { match self.channel.recv().await.unwrap() { ToCache::Query(data) => { for id in data.ids { if id == ENTRY { let mut holder = HashMap::new(); holder.insert(ENTRY.to_string(), DataType::new("store").unwrap()); data.reply.send(FromCache::Data(holder)).await.unwrap(); } else { data.reply .send(FromCache::Error(MTTError::new("fred"))) .await .unwrap(); } } } } } } } #[cfg(test)] mod caches { use super::*; use tempfile::tempdir; async fn start_cache

(dir: P) -> Sender where P: Into, { let (s, r) = unbounded(); let datadir = dir.into(); spawn(async move { let cache = Cache::new(datadir, r).await.unwrap(); cache.start().await; }); s } async fn send_request(data: Vec<&str>, channel: Sender) -> FromCache { let mut ids = Vec::new(); for id in data.iter() { ids.push(id.to_string()); } let (s, r) = unbounded(); let msg = ToCache::Query( CacheQuery { ids: ids, reply: s, }); channel.send(msg).await.unwrap(); r.recv().await.unwrap() } #[async_std::test] async fn create() { let dir = tempdir().unwrap(); let s_cache = start_cache(dir.path()).await; let result = send_request(vec![ENTRY], s_cache).await; match result { FromCache::Data(data) => match data.get(ENTRY) { Some(output) => match output { DataType::DBMap(_) => (), _ => assert!(false, "{:?} is not a database store.", output), }, None => assert!(false, "Should contain entry point."), }, _ => assert!(false, "{:?} should have been a store.", result), } } #[async_std::test] async fn bad_entry() { let dir = tempdir().unwrap(); let s_cache = start_cache(dir.path()).await; let result = send_request(vec!["bad_id"], s_cache).await; match result { FromCache::Error(_) => (), _ => assert!(false, "{:?} should have been an error.", result), } } } pub async fn start_db

(_dir: P) -> Result where P: Into, { let (s, r) = unbounded(); spawn(async move { loop { r.recv().await.unwrap(); } }); Ok(MoreThanText::new(s).await.unwrap()) } #[cfg(test)] mod db_start_up { use super::*; use tempfile::tempdir; #[async_std::test] async fn initial_session() { let dir = tempdir().unwrap(); let mtt = start_db(dir.path()).await.unwrap(); assert_eq!(mtt.session, [ENTRY]); } }