diff --git a/src/morethantext/cache.rs b/src/morethantext/cache.rs
new file mode 100644
index 0000000..62ad209
--- /dev/null
+++ b/src/morethantext/cache.rs
@@ -0,0 +1,30 @@
+use async_std::{channel::Receiver, path::PathBuf};
+
+pub struct Cache;
+
+impl Cache {
+ pub async fn new
(_dir: P) -> Self
+ where
+ P: Into,
+ {
+ Self {}
+ }
+
+ pub async fn listen(&self, listener: Receiver) {
+ loop {
+ listener.recv().await.unwrap();
+ }
+ }
+}
+
+#[cfg(test)]
+mod engine {
+ use super::*;
+ use tempfile::tempdir;
+
+ #[async_std::test]
+ async fn create() {
+ let dir = tempdir().unwrap();
+ Cache::new(dir.path()).await;
+ }
+}
diff --git a/src/morethantext/error.rs b/src/morethantext/error.rs
index ed37284..682c980 100644
--- a/src/morethantext/error.rs
+++ b/src/morethantext/error.rs
@@ -1,95 +1,57 @@
-use async_std::path::PathBuf;
use std::{error::Error, fmt};
#[derive(Debug)]
pub enum ErrorCode {
+ // General
Undefined(String),
- // Read Write Errors
- CorruptFile,
- // Data Type Errors
- DataTypeIncorrect(String),
- // Entry Errors
- EntryExists(PathBuf),
- EntryWriteFailure(PathBuf),
- EntryReadFailure(PathBuf),
- EntryDeleteFailure(PathBuf),
- // Cache
- CacheReadWrite,
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorCode::Undefined(msg) => write!(f, "{}", msg),
- ErrorCode::DataTypeIncorrect(dtype) => write!(f, "data type '{}' is not valid", dtype),
- ErrorCode::CorruptFile => write!(f, "corrupt file"),
- ErrorCode::EntryExists(path) => write!(
- f,
- "entry '{}' already exists",
- path.file_name().unwrap().to_str().unwrap()
- ),
- ErrorCode::EntryWriteFailure(path) => write!(
- f,
- "entry '{}' write failure",
- path.file_name().unwrap().to_str().unwrap()
- ),
- ErrorCode::EntryReadFailure(path) => write!(
- f,
- "entry '{}' read failure",
- path.file_name().unwrap().to_str().unwrap()
- ),
- ErrorCode::EntryDeleteFailure(path) => write!(
- f,
- "entry '{}' delete failure",
- path.file_name().unwrap().to_str().unwrap()
- ),
- ErrorCode::CacheReadWrite => write!(f, "cache read write"),
+ }
+ }
+}
+
+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);
}
}
}
#[derive(Debug)]
-pub struct DBError {
- pub code: ErrorCode,
- src: Option>,
+pub struct MTTError {
+ code: ErrorCode,
}
-impl DBError {
- pub fn new(msg: S) -> Self
+impl MTTError {
+ fn new(msg: S) -> Self
where
S: Into,
{
+ let text = msg.into();
Self {
- code: ErrorCode::Undefined(msg.into()),
- src: None,
+ code: ErrorCode::Undefined(text),
}
}
- pub fn from_code(code: ErrorCode) -> Self {
- Self {
- code: code,
- src: None,
- }
- }
-
- pub fn add_source(&mut self, src: E)
- where
- E: Error + 'static,
- {
- self.src = Some(Box::new(src));
+ fn from_code(code: ErrorCode) -> Self {
+ Self { code: code }
}
}
-impl Error for DBError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match &self.src {
- Some(err) => Some(err.as_ref()),
- None => None,
- }
- }
-}
+impl Error for MTTError {}
-impl fmt::Display for DBError {
+impl fmt::Display for MTTError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.code)
}
@@ -100,160 +62,28 @@ mod errors {
use super::*;
#[test]
- fn with_str() {
- let msg = "something happened";
- let err = DBError::new(msg);
- assert!(
- err.to_string() == msg,
- "Got: {} -- Want: {}",
- err.to_string(),
- msg
- );
- assert!(
- err.source().is_none(),
- "Error should initialize with no source."
- );
+ 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 with_string() {
- let msg = "it went boom".to_string();
- let err = DBError::new(msg.clone());
- assert!(
- err.to_string() == msg,
- "Got: {} -- Want: {}",
- err.to_string(),
- msg
- );
- assert!(
- err.source().is_none(),
- "Error should initialize with no source."
- );
- }
-
- #[test]
- fn using_error_code() {
- let msg = "utter failure";
- let code = ErrorCode::Undefined(msg.to_string());
- let err = DBError::from_code(code);
+ fn create_with_string() {
+ let msg = "three";
+ let err = MTTError::new(msg.to_string());
assert_eq!(err.to_string(), msg);
- assert!(err.source().is_none(), "Should be no source");
}
#[test]
- fn with_source() {
- let msg = "but this caused the problem";
- let mut par = DBError::new("parent error");
- let src = DBError::new(msg);
- par.add_source(src);
- let output = par.source();
- assert!(output.is_some(), "Should return source.");
- let source = output.unwrap();
- assert!(source.to_string() == msg);
- }
-}
-
-#[cfg(test)]
-mod codes {
- use super::*;
- use async_std::path::PathBuf;
-
- const ITEMS: [&str; 2] = ["first", "second"];
-
- fn create_path_buffer() -> Vec {
- let mut output = Vec::new();
- for item in ITEMS {
- let mut path = PathBuf::new();
- path.push("thepath");
- path.push(item);
- output.push(path);
- }
- output
- }
-
- #[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::DataTypeIncorrect(item.to_string());
- assert_eq!(
- err.to_string(),
- format!("data type '{}' is not valid", item)
- );
- }
- }
-
- #[test]
- fn corrupt_file() {
- assert_eq!(ErrorCode::CorruptFile.to_string(), "corrupt file");
- }
-
- #[test]
- fn entry_exists() {
- for path in create_path_buffer() {
- let err = ErrorCode::EntryExists(path.clone());
- assert_eq!(
- err.to_string(),
- format!(
- "entry '{}' already exists",
- path.file_name().unwrap().to_str().unwrap()
- )
- );
- }
- }
-
- #[test]
- fn entry_write_failure() {
- for path in create_path_buffer() {
- let err = ErrorCode::EntryWriteFailure(path.clone());
- assert_eq!(
- err.to_string(),
- format!(
- "entry '{}' write failure",
- path.file_name().unwrap().to_str().unwrap()
- )
- );
- }
- }
-
- #[test]
- fn entry_read_failure() {
- for path in create_path_buffer() {
- let err = ErrorCode::EntryReadFailure(path.clone());
- assert_eq!(
- err.to_string(),
- format!(
- "entry '{}' read failure",
- path.file_name().unwrap().to_str().unwrap()
- )
- );
- }
- }
-
- #[test]
- fn entry_delete_failure() {
- for path in create_path_buffer() {
- let err = ErrorCode::EntryDeleteFailure(path.clone());
- assert_eq!(
- err.to_string(),
- format!(
- "entry '{}' delete failure",
- path.file_name().unwrap().to_str().unwrap()
- )
- );
- }
- }
-
- #[test]
- fn cache_read_write_failure() {
- let err = ErrorCode::CacheReadWrite;
- assert_eq!(err.to_string(), "cache read write");
+ 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),
+ }
}
}
diff --git a/src/morethantext/mod-3.rs b/src/morethantext/mod-3.rs
new file mode 100644
index 0000000..2fd4479
--- /dev/null
+++ b/src/morethantext/mod-3.rs
@@ -0,0 +1,640 @@
+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 {
+ // General
+ Undefined(String),
+ // Cache
+ EntryNotFound(String),
+ InvalidCommitData,
+ // Store
+ DatabaseAlreadyExists(String),
+}
+
+impl fmt::Display for ErrorCode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ErrorCode::Undefined(msg) => write!(f, "{}", msg),
+ ErrorCode::EntryNotFound(id) => write!(f, "entry '{}' was not found", id),
+ ErrorCode::InvalidCommitData => write!(f, "commit data was not a database store"),
+ ErrorCode::DatabaseAlreadyExists(name) => {
+ write!(f, "database '{}' already exists", name)
+ }
+ }
+ }
+}
+
+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 bad_entry() {
+ for item in ITEMS {
+ let err = ErrorCode::EntryNotFound(item.to_string());
+ assert_eq!(err.to_string(), format!("entry '{}' was not found", item));
+ }
+ }
+
+ #[test]
+ fn invalid_commit_data() {
+ let err = ErrorCode::InvalidCommitData;
+ assert_eq!(err.to_string(), "commit data was not a database store");
+ }
+
+ #[test]
+ fn database_already_exists() {
+ for item in ITEMS {
+ let err = ErrorCode::DatabaseAlreadyExists(item.to_string());
+ assert_eq!(
+ err.to_string(),
+ format!("database '{}' already exists", 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_missing_entry() {
+ let code = ErrorCode::EntryNotFound("an_id".to_string());
+ let err = MTTError::from_code(code);
+ match err.code {
+ ErrorCode::EntryNotFound(_) => (),
+ _ => assert!(false, "{:?} is not undefined", err.code),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+struct Storage {
+ id: Option,
+ data: Option,
+ // delete: bool,
+}
+
+impl Storage {
+ fn from_id(id: S) -> Self
+ where
+ S: Into,
+ {
+ Self {
+ id: Some(id.into()),
+ data: None,
+ }
+ }
+
+ fn from_datatype(dt: DataType) -> Self {
+ Self {
+ id: None,
+ data: Some(dt),
+ }
+ }
+}
+
+#[cfg(test)]
+mod storage {
+ use super::*;
+
+ #[test]
+ fn from_id_with_str() {
+ let ids = ["first", "second"];
+ for id in ids {
+ let output = Storage::from_id(id);
+ assert_eq!(output.id, Some(id.to_string()));
+ assert!(
+ output.data.is_none(),
+ "The storage data should have been Non."
+ );
+ }
+ }
+
+ #[test]
+ fn from_id_with_string() {
+ let id = "my_id".to_string();
+ let output = Storage::from_id(id.clone());
+ assert_eq!(output.id, Some(id));
+ }
+
+ #[test]
+ fn from_store() {
+ let output = Storage::from_datatype(DataType::new("store"));
+ assert!(output.id.is_none(), "id should be None.");
+ assert!(output.data.is_some(), "There should be data");
+ let result = output.data.unwrap();
+ match result {
+ DataType::DBMap(_) => (),
+ _ => assert!(false, "{:?} should have been DataType::DBMap.", result),
+ }
+ }
+
+ #[test]
+ fn from_database() {
+ let output = Storage::from_datatype(DataType::new("database"));
+ let result = output.data.unwrap();
+ match result {
+ DataType::TableMap(_) => (),
+ _ => assert!(false, "{:?} should have been DataType::TableMap.", result),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+struct Store {
+ data: HashMap>,
+}
+
+impl Store {
+ fn new() -> Self {
+ Self {
+ data: HashMap::new(),
+ }
+ }
+
+ fn add_new(&mut self, name: S) -> Result<(), MTTError>
+ where
+ S: Into,
+ {
+ let dbname = name.into();
+ match self.get(&dbname) {
+ Some(_) => Err(MTTError::from_code(ErrorCode::DatabaseAlreadyExists(
+ dbname,
+ ))),
+ None => {
+ self.data
+ .insert(dbname, Storage::from_datatype(DataType::new("database")));
+ Ok(())
+ }
+ }
+ }
+
+ fn get(&self, name: &str) -> Option<&Storage> {
+ self.data.get(name)
+ }
+}
+
+#[cfg(test)]
+mod stores {
+ use super::*;
+
+ #[test]
+ fn get_no_database() -> Result<(), MTTError> {
+ let store = Store::new();
+ match store.get("missing_name") {
+ Some(_) => Err(MTTError::new("should have returned None")),
+ None => Ok(()),
+ }
+ }
+
+ #[test]
+ fn add_database_str() {
+ let mut store = Store::new();
+ let names = ["first", "second"];
+ for name in names {
+ store.add_new(name).unwrap();
+ let output = store.get(name).unwrap();
+ assert!(output.data.is_some(), "There should be a data type.");
+ match output.data.clone().unwrap() {
+ DataType::TableMap(_) => (),
+ _ => assert!(
+ false,
+ "{:?} should have been DataType::TableMap.",
+ output.data
+ ),
+ }
+ assert!(output.id.is_none(), "Should not have an id.");
+ }
+ }
+
+ #[test]
+ fn add_database_string() {
+ let mut store = Store::new();
+ let name = "third".to_string();
+ store.add_new(name.clone()).unwrap();
+ let output = store.get(&name).unwrap();
+ match output.data.clone().unwrap() {
+ DataType::TableMap(_) => (),
+ _ => assert!(
+ false,
+ "{:?} should have been DataType::TableMap.",
+ output.data
+ ),
+ }
+ }
+
+ #[test]
+ fn no_duplicate_database_names() -> Result<(), MTTError> {
+ let mut store = Store::new();
+ let name = "duplicate";
+ store.add_new(name).unwrap();
+ match store.add_new(name) {
+ Ok(_) => Err(MTTError::new("should have been an error")),
+ Err(err) => match err.code {
+ ErrorCode::DatabaseAlreadyExists(dbname) => {
+ assert_eq!(dbname, name);
+ Ok(())
+ }
+ _ => Err(MTTError::new(format!(
+ "{:?} should have been DatabaseAlreadyExists.",
+ err.code
+ ))),
+ },
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+struct Database;
+
+#[cfg(test)]
+mod databases {
+ use super::*;
+
+ #[test]
+ fn create() {
+ Database::new();
+ }
+}
+
+impl Database {
+ fn new() -> Self {
+ Self {}
+ }
+}
+
+#[derive(Clone, Debug)]
+enum DataType {
+ DBMap(Store),
+ TableMap(Database),
+}
+
+impl DataType {
+ fn new(dtype: &str) -> DataType {
+ match dtype {
+ "store" => Self::DBMap(Store::new()),
+ "database" => Self::TableMap(Database::new()),
+ _ => unreachable!(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod datatypes {
+ use super::*;
+
+ #[test]
+ fn create_store() {
+ let dtype = DataType::new("store");
+ match dtype {
+ DataType::DBMap(_) => (),
+ _ => assert!(false, "{:?} is not incorrect data type", dtype),
+ }
+ }
+
+ #[test]
+ fn create_database() {
+ let dtype = DataType::new("database");
+ match dtype {
+ DataType::TableMap(_) => (),
+ _ => assert!(false, "{:?} is not incorrect data type", dtype),
+ }
+ }
+}
+
+#[derive(Debug)]
+enum FromCache {
+ Ok,
+ Data(HashMap),
+ Error(MTTError),
+}
+
+struct CacheQuery {
+ ids: Vec,
+ reply: Sender,
+}
+
+struct CacheCommit {
+ reply: Sender,
+ data: DataType,
+}
+
+impl CacheCommit {
+ fn new(data: DataType, channel: Sender) -> Result {
+ match data {
+ DataType::DBMap(_) => (),
+ _ => return Err(MTTError::from_code(ErrorCode::InvalidCommitData)),
+ }
+ Ok(Self {
+ data: data,
+ reply: channel,
+ })
+ }
+}
+
+mod commits {
+ use super::*;
+
+ #[test]
+ fn create() -> Result<(), MTTError> {
+ let (s, _) = unbounded();
+ match CacheCommit::new(DataType::new("store"), s) {
+ Ok(output) => match output.data {
+ DataType::DBMap(_) => Ok(()),
+ _ => Err(MTTError::new(format!(
+ "{:?} should have been DBMap",
+ output.data
+ ))),
+ },
+ Err(err) => Err(err),
+ }
+ }
+
+ #[test]
+ fn bad_data_type() -> Result<(), MTTError> {
+ let (s, _) = unbounded();
+ match CacheCommit::new(DataType::new("database"), s) {
+ Ok(_) => Err(MTTError::new("CacheCommit::new did not return error")),
+ Err(err) => match err.code {
+ ErrorCode::InvalidCommitData => Ok(()),
+ _ => Err(MTTError::new(format!(
+ "{:?} is not the correct error",
+ err.code
+ ))),
+ },
+ }
+ }
+}
+
+enum ToCache {
+ Query(CacheQuery),
+ Commit(CacheCommit),
+}
+
+#[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;
+
+impl Cache {
+ async fn new(_dir: P) -> Result
+ where
+ P: Into,
+ {
+ Ok(Self {})
+ }
+
+ async fn query(&self, qry: &Vec) -> Result, MTTError> {
+ let mut output = HashMap::new();
+ for id in qry {
+ if id == ENTRY {
+ output.insert(ENTRY.to_string(), DataType::new("store"));
+ } else {
+ return Err(MTTError::from_code(ErrorCode::EntryNotFound(
+ id.to_string(),
+ )));
+ }
+ }
+ Ok(output)
+ }
+
+ async fn commit(&self) -> Result<(), MTTError> {
+ Ok(())
+ }
+
+ async fn start(&self, listener: Receiver) {
+ loop {
+ match listener.recv().await.unwrap() {
+ ToCache::Query(qry) => match self.query(&qry.ids).await {
+ Ok(data) => qry.reply.send(FromCache::Data(data)).await.unwrap(),
+ Err(error) => qry.reply.send(FromCache::Error(error)).await.unwrap(),
+ },
+ ToCache::Commit(commit) => match self.commit().await {
+ Ok(_) => commit.reply.send(FromCache::Ok).await.unwrap(),
+ Err(error) => commit.reply.send(FromCache::Error(error)).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).await.unwrap();
+ cache.start(r).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),
+ }
+ }
+
+ #[async_std::test]
+ async fn empty_commit() {
+ let dir = tempdir().unwrap();
+ let s_cache = start_cache(dir.path()).await;
+ let (s, r) = unbounded();
+ let msg = ToCache::Commit(CacheCommit::new(DataType::new("store"), s).unwrap());
+ s_cache.send(msg).await.unwrap();
+ let result = r.recv().await.unwrap();
+ match result {
+ FromCache::Ok => (),
+ _ => assert!(false, "{:?} should have been an Ok.", result),
+ }
+ }
+
+ #[async_std::test]
+ async fn get_store() {
+ let dir = tempdir().unwrap();
+ let cache = Cache::new(dir.path()).await.unwrap();
+ let output = cache.query(&[ENTRY.to_string()].to_vec()).await.unwrap();
+ let result = output.get(ENTRY).unwrap();
+ match result {
+ DataType::DBMap(_) => (),
+ _ => assert!(false, "{:?} should have been an Ok.", result),
+ }
+ }
+
+ #[async_std::test]
+ async fn bad_get() {
+ let dir = tempdir().unwrap();
+ let cache = Cache::new(dir.path()).await.unwrap();
+ let bad_id = "really_bad_id";
+ match cache.query(&[bad_id.to_string()].to_vec()).await {
+ Ok(_) => assert!(false, "Should have produced an error."),
+ Err(err) => match err.code {
+ ErrorCode::EntryNotFound(id) => assert_eq!(id, bad_id),
+ _ => assert!(false, "{:?} should have been EntryNotFound.", err.code),
+ },
+ }
+ }
+}
+
+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]);
+ }
+}
diff --git a/src/morethantext/mod.rs b/src/morethantext/mod.rs
index f10c14b..3e5746d 100644
--- a/src/morethantext/mod.rs
+++ b/src/morethantext/mod.rs
@@ -1,650 +1,55 @@
+mod cache;
+mod error;
+
use async_std::{
- channel::{unbounded, Receiver, Sender},
+ channel::{unbounded, Sender},
path::PathBuf,
task::spawn,
};
-use std::{collections::HashMap, error::Error, fmt};
-
-const ENTRY: &str = "EntryPoint";
-
-trait Requests {
- fn add(kind: &str, key: &str, value: Storage) -> Result<(), MTTError> {
- Err(MTTError::new("not supported"))
- }
-
- fn get() -> Vec {
- Vec::new()
- }
-}
-
-#[derive(Debug)]
-enum ErrorCode {
- // General
- Undefined(String),
- // Cache
- EntryNotFound(String),
- InvalidCommitData,
- // Store
- DatabaseAlreadyExists(String),
-}
-
-impl fmt::Display for ErrorCode {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- ErrorCode::Undefined(msg) => write!(f, "{}", msg),
- ErrorCode::EntryNotFound(id) => write!(f, "entry '{}' was not found", id),
- ErrorCode::InvalidCommitData => write!(f, "commit data was not a database store"),
- ErrorCode::DatabaseAlreadyExists(name) => {
- write!(f, "database '{}' already exists", name)
- }
- }
- }
-}
-
-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 bad_entry() {
- for item in ITEMS {
- let err = ErrorCode::EntryNotFound(item.to_string());
- assert_eq!(err.to_string(), format!("entry '{}' was not found", item));
- }
- }
-
- #[test]
- fn invalid_commit_data() {
- let err = ErrorCode::InvalidCommitData;
- assert_eq!(err.to_string(), "commit data was not a database store");
- }
-
- #[test]
- fn database_already_exists() {
- for item in ITEMS {
- let err = ErrorCode::DatabaseAlreadyExists(item.to_string());
- assert_eq!(
- err.to_string(),
- format!("database '{}' already exists", 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_missing_entry() {
- let code = ErrorCode::EntryNotFound("an_id".to_string());
- let err = MTTError::from_code(code);
- match err.code {
- ErrorCode::EntryNotFound(_) => (),
- _ => assert!(false, "{:?} is not undefined", err.code),
- }
- }
-}
+use cache::Cache;
+use error::{ErrorCode, MTTError};
#[derive(Clone, Debug)]
-struct Storage {
- id: Option,
- data: Option,
- // delete: bool,
-}
-
-impl Storage {
- fn from_id(id: S) -> Self
- where
- S: Into,
- {
- Self {
- id: Some(id.into()),
- data: None,
- }
- }
-
- fn from_datatype(dt: DataType) -> Self {
- Self {
- id: None,
- data: Some(dt),
- }
- }
-}
-
-#[cfg(test)]
-mod storage {
- use super::*;
-
- #[test]
- fn from_id_with_str() {
- let ids = ["first", "second"];
- for id in ids {
- let output = Storage::from_id(id);
- assert_eq!(output.id, Some(id.to_string()));
- assert!(
- output.data.is_none(),
- "The storage data should have been Non."
- );
- }
- }
-
- #[test]
- fn from_id_with_string() {
- let id = "my_id".to_string();
- let output = Storage::from_id(id.clone());
- assert_eq!(output.id, Some(id));
- }
-
- #[test]
- fn from_store() {
- let output = Storage::from_datatype(DataType::new("store"));
- assert!(output.id.is_none(), "id should be None.");
- assert!(output.data.is_some(), "There should be data");
- let result = output.data.unwrap();
- match result {
- DataType::DBMap(_) => (),
- _ => assert!(false, "{:?} should have been DataType::DBMap.", result),
- }
- }
-
- #[test]
- fn from_database() {
- let output = Storage::from_datatype(DataType::new("database"));
- let result = output.data.unwrap();
- match result {
- DataType::TableMap(_) => (),
- _ => assert!(false, "{:?} should have been DataType::TableMap.", result),
- }
- }
-}
-
-#[derive(Clone, Debug)]
-struct Store {
- data: HashMap,
-}
-
-impl Store {
- fn new() -> Self {
- Self {
- data: HashMap::new(),
- }
- }
-
- fn add_new(&mut self, name: S) -> Result<(), MTTError>
- where
- S: Into,
- {
- let dbname = name.into();
- match self.get(&dbname) {
- Some(_) => Err(MTTError::from_code(ErrorCode::DatabaseAlreadyExists(
- dbname,
- ))),
- None => {
- self.data
- .insert(dbname, Storage::from_datatype(DataType::new("database")));
- Ok(())
- }
- }
- }
-
- fn get(&self, name: &str) -> Option<&Storage> {
- self.data.get(name)
- }
-}
-
-#[cfg(test)]
-mod stores {
- use super::*;
-
- #[test]
- fn get_no_database() -> Result<(), MTTError> {
- let store = Store::new();
- match store.get("missing_name") {
- Some(_) => Err(MTTError::new("should have returned None")),
- None => Ok(()),
- }
- }
-
- #[test]
- fn add_database_str() {
- let mut store = Store::new();
- let names = ["first", "second"];
- for name in names {
- store.add_new(name).unwrap();
- let output = store.get(name).unwrap();
- assert!(output.data.is_some(), "There should be a data type.");
- match output.data.clone().unwrap() {
- DataType::TableMap(_) => (),
- _ => assert!(
- false,
- "{:?} should have been DataType::TableMap.",
- output.data
- ),
- }
- assert!(output.id.is_none(), "Should not have an id.");
- }
- }
-
- #[test]
- fn add_database_string() {
- let mut store = Store::new();
- let name = "third".to_string();
- store.add_new(name.clone()).unwrap();
- let output = store.get(&name).unwrap();
- match output.data.clone().unwrap() {
- DataType::TableMap(_) => (),
- _ => assert!(
- false,
- "{:?} should have been DataType::TableMap.",
- output.data
- ),
- }
- }
-
- #[test]
- fn no_duplicate_database_names() -> Result<(), MTTError> {
- let mut store = Store::new();
- let name = "duplicate";
- store.add_new(name).unwrap();
- match store.add_new(name) {
- Ok(_) => Err(MTTError::new("should have been an error")),
- Err(err) => match err.code {
- ErrorCode::DatabaseAlreadyExists(dbname) => {
- assert_eq!(dbname, name);
- Ok(())
- }
- _ => Err(MTTError::new(format!(
- "{:?} should have been DatabaseAlreadyExists.",
- err.code
- ))),
- },
- }
- }
-}
-
-#[derive(Clone, Debug)]
-struct Database;
-
-#[cfg(test)]
-mod databases {
- use super::*;
-
- #[test]
- fn create() {
- Database::new();
- }
-}
-
-impl Database {
- fn new() -> Self {
- Self {}
- }
-}
-
-#[derive(Clone, Debug)]
-enum DataType {
- DBMap(Store),
- TableMap(Database),
-}
-
-impl DataType {
- fn new(dtype: &str) -> DataType {
- match dtype {
- "store" => Self::DBMap(Store::new()),
- "database" => Self::TableMap(Database::new()),
- _ => unreachable!(),
- }
- }
-}
-
-#[cfg(test)]
-mod datatypes {
- use super::*;
-
- #[test]
- fn create_store() {
- let dtype = DataType::new("store");
- match dtype {
- DataType::DBMap(_) => (),
- _ => assert!(false, "{:?} is not incorrect data type", dtype),
- }
- }
-
- #[test]
- fn create_database() {
- let dtype = DataType::new("database");
- match dtype {
- DataType::TableMap(_) => (),
- _ => assert!(false, "{:?} is not incorrect data type", dtype),
- }
- }
-}
-
-#[derive(Debug)]
-enum FromCache {
- Ok,
- Data(HashMap),
- Error(MTTError),
-}
-
-struct CacheQuery {
- ids: Vec,
- reply: Sender,
-}
-
-struct CacheCommit {
- reply: Sender,
- data: DataType,
-}
-
-impl CacheCommit {
- fn new(data: DataType, channel: Sender) -> Result {
- match data {
- DataType::DBMap(_) => (),
- _ => return Err(MTTError::from_code(ErrorCode::InvalidCommitData)),
- }
- Ok(Self {
- data: data,
- reply: channel,
- })
- }
-}
-
-mod commits {
- use super::*;
-
- #[test]
- fn create() -> Result<(), MTTError> {
- let (s, _) = unbounded();
- match CacheCommit::new(DataType::new("store"), s) {
- Ok(output) => match output.data {
- DataType::DBMap(_) => Ok(()),
- _ => Err(MTTError::new(format!(
- "{:?} should have been DBMap",
- output.data
- ))),
- },
- Err(err) => Err(err),
- }
- }
-
- #[test]
- fn bad_data_type() -> Result<(), MTTError> {
- let (s, _) = unbounded();
- match CacheCommit::new(DataType::new("database"), s) {
- Ok(_) => Err(MTTError::new("CacheCommit::new did not return error")),
- Err(err) => match err.code {
- ErrorCode::InvalidCommitData => Ok(()),
- _ => Err(MTTError::new(format!(
- "{:?} is not the correct error",
- err.code
- ))),
- },
- }
- }
-}
-
-enum ToCache {
- Query(CacheQuery),
- Commit(CacheCommit),
+struct Data {
+ id: String,
}
#[derive(Clone)]
pub struct MoreThanText {
- session: Vec,
- cache: Sender>,
+ to_cache: Sender,
}
impl MoreThanText {
- async fn new(cache: Sender>) -> Result {
- Ok(Self {
- session: [ENTRY.to_string()].to_vec(),
- cache: cache,
- })
+ fn new(to_cache: Sender) -> Self {
+ Self { to_cache: to_cache }
+ }
+
+ async fn session(&self) {
}
}
#[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;
-
-impl Cache {
- async fn new(_dir: P) -> Result
- where
- P: Into,
- {
- Ok(Self {})
- }
-
- async fn query(&self, qry: &Vec) -> Result, MTTError> {
- let mut output = HashMap::new();
- for id in qry {
- if id == ENTRY {
- output.insert(ENTRY.to_string(), DataType::new("store"));
- } else {
- return Err(MTTError::from_code(ErrorCode::EntryNotFound(
- id.to_string(),
- )));
- }
- }
- Ok(output)
- }
-
- async fn commit(&self) -> Result<(), MTTError> {
- Ok(())
- }
-
- async fn start(&self, listener: Receiver) {
- loop {
- match listener.recv().await.unwrap() {
- ToCache::Query(qry) => match self.query(&qry.ids).await {
- Ok(data) => qry.reply.send(FromCache::Data(data)).await.unwrap(),
- Err(error) => qry.reply.send(FromCache::Error(error)).await.unwrap(),
- },
- ToCache::Commit(commit) => match self.commit().await {
- Ok(_) => commit.reply.send(FromCache::Ok).await.unwrap(),
- Err(error) => commit.reply.send(FromCache::Error(error)).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).await.unwrap();
- cache.start(r).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() {
+ async fn create_new() {
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),
- }
- }
-
- #[async_std::test]
- async fn empty_commit() {
- let dir = tempdir().unwrap();
- let s_cache = start_cache(dir.path()).await;
- let (s, r) = unbounded();
- let msg = ToCache::Commit(CacheCommit::new(DataType::new("store"), s).unwrap());
- s_cache.send(msg).await.unwrap();
- let result = r.recv().await.unwrap();
- match result {
- FromCache::Ok => (),
- _ => assert!(false, "{:?} should have been an Ok.", result),
- }
- }
-
- #[async_std::test]
- async fn get_store() {
- let dir = tempdir().unwrap();
- let cache = Cache::new(dir.path()).await.unwrap();
- let output = cache.query(&[ENTRY.to_string()].to_vec()).await.unwrap();
- let result = output.get(ENTRY).unwrap();
- match result {
- DataType::DBMap(_) => (),
- _ => assert!(false, "{:?} should have been an Ok.", result),
- }
- }
-
- #[async_std::test]
- async fn bad_get() {
- let dir = tempdir().unwrap();
- let cache = Cache::new(dir.path()).await.unwrap();
- let bad_id = "really_bad_id";
- match cache.query(&[bad_id.to_string()].to_vec()).await {
- Ok(_) => assert!(false, "Should have produced an error."),
- Err(err) => match err.code {
- ErrorCode::EntryNotFound(id) => assert_eq!(id, bad_id),
- _ => assert!(false, "{:?} should have been EntryNotFound.", err.code),
- },
- }
+ let mtt = start_db(dir.path()).await.unwrap();
+ mtt.session().await;
}
}
-pub async fn start_db(_dir: P) -> Result
+pub async fn start_db(dir: P) -> Result
where
P: Into,
{
+ let path = dir.into();
let (s, r) = unbounded();
spawn(async move {
- loop {
- r.recv().await.unwrap();
- }
+ let cache = Cache::new(path).await;
+ cache.listen(r).await;
});
- 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]);
- }
+ Ok(MoreThanText::new(s))
}