Setting up for data storage.
This commit is contained in:
		
							
								
								
									
										30
									
								
								src/morethantext/cache.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/morethantext/cache.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
use async_std::{channel::Receiver, path::PathBuf};
 | 
			
		||||
 | 
			
		||||
pub struct Cache;
 | 
			
		||||
 | 
			
		||||
impl Cache {
 | 
			
		||||
    pub async fn new<P>(_dir: P) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        P: Into<PathBuf>,
 | 
			
		||||
    {
 | 
			
		||||
        Self {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn listen(&self, listener: Receiver<String>) {
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<Box<dyn Error + 'static>>,
 | 
			
		||||
pub struct MTTError {
 | 
			
		||||
    code: ErrorCode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DBError {
 | 
			
		||||
    pub fn new<S>(msg: S) -> Self
 | 
			
		||||
impl MTTError {
 | 
			
		||||
    fn new<S>(msg: S) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        S: Into<String>,
 | 
			
		||||
    {
 | 
			
		||||
        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<E>(&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<PathBuf> {
 | 
			
		||||
        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),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										640
									
								
								src/morethantext/mod-3.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										640
									
								
								src/morethantext/mod-3.rs
									
									
									
									
									
										Normal file
									
								
							@@ -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<S>(msg: S) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        S: Into<String>,
 | 
			
		||||
    {
 | 
			
		||||
        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<D> {
 | 
			
		||||
    id: Option<String>,
 | 
			
		||||
    data: Option<D>,
 | 
			
		||||
    // delete: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Storage<D> {
 | 
			
		||||
    fn from_id<S>(id: S) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        S: Into<String>,
 | 
			
		||||
    {
 | 
			
		||||
        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<String, Storage<Database>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Store {
 | 
			
		||||
    fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            data: HashMap::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn add_new<S>(&mut self, name: S) -> Result<(), MTTError>
 | 
			
		||||
    where
 | 
			
		||||
        S: Into<String>,
 | 
			
		||||
    {
 | 
			
		||||
        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<Database>> {
 | 
			
		||||
        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<String, DataType>),
 | 
			
		||||
    Error(MTTError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct CacheQuery {
 | 
			
		||||
    ids: Vec<String>,
 | 
			
		||||
    reply: Sender<FromCache>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct CacheCommit {
 | 
			
		||||
    reply: Sender<FromCache>,
 | 
			
		||||
    data: DataType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CacheCommit {
 | 
			
		||||
    fn new(data: DataType, channel: Sender<FromCache>) -> Result<Self, MTTError> {
 | 
			
		||||
        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<String>,
 | 
			
		||||
    cache: Sender<Vec<String>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MoreThanText {
 | 
			
		||||
    async fn new(cache: Sender<Vec<String>>) -> Result<Self, MTTError> {
 | 
			
		||||
        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<P>(_dir: P) -> Result<Self, MTTError>
 | 
			
		||||
    where
 | 
			
		||||
        P: Into<PathBuf>,
 | 
			
		||||
    {
 | 
			
		||||
        Ok(Self {})
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn query(&self, qry: &Vec<String>) -> Result<HashMap<String, DataType>, 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<ToCache>) {
 | 
			
		||||
        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<P>(dir: P) -> Sender<ToCache>
 | 
			
		||||
    where
 | 
			
		||||
        P: Into<PathBuf>,
 | 
			
		||||
    {
 | 
			
		||||
        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<ToCache>) -> 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<P>(_dir: P) -> Result<MoreThanText, MTTError>
 | 
			
		||||
where
 | 
			
		||||
    P: Into<PathBuf>,
 | 
			
		||||
{
 | 
			
		||||
    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]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<Storage> {
 | 
			
		||||
        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<S>(msg: S) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        S: Into<String>,
 | 
			
		||||
    {
 | 
			
		||||
        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<String>,
 | 
			
		||||
    data: Option<DataType>,
 | 
			
		||||
    // delete: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Storage {
 | 
			
		||||
    fn from_id<S>(id: S) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        S: Into<String>,
 | 
			
		||||
    {
 | 
			
		||||
        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<String, Storage>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Store {
 | 
			
		||||
    fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            data: HashMap::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn add_new<S>(&mut self, name: S) -> Result<(), MTTError>
 | 
			
		||||
    where
 | 
			
		||||
        S: Into<String>,
 | 
			
		||||
    {
 | 
			
		||||
        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<String, DataType>),
 | 
			
		||||
    Error(MTTError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct CacheQuery {
 | 
			
		||||
    ids: Vec<String>,
 | 
			
		||||
    reply: Sender<FromCache>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct CacheCommit {
 | 
			
		||||
    reply: Sender<FromCache>,
 | 
			
		||||
    data: DataType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CacheCommit {
 | 
			
		||||
    fn new(data: DataType, channel: Sender<FromCache>) -> Result<Self, MTTError> {
 | 
			
		||||
        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<String>,
 | 
			
		||||
    cache: Sender<Vec<String>>,
 | 
			
		||||
    to_cache: Sender<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MoreThanText {
 | 
			
		||||
    async fn new(cache: Sender<Vec<String>>) -> Result<Self, MTTError> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            session: [ENTRY.to_string()].to_vec(),
 | 
			
		||||
            cache: cache,
 | 
			
		||||
        })
 | 
			
		||||
    fn new(to_cache: Sender<String>) -> 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<P>(_dir: P) -> Result<Self, MTTError>
 | 
			
		||||
    where
 | 
			
		||||
        P: Into<PathBuf>,
 | 
			
		||||
    {
 | 
			
		||||
        Ok(Self {})
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn query(&self, qry: &Vec<String>) -> Result<HashMap<String, DataType>, 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<ToCache>) {
 | 
			
		||||
        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<P>(dir: P) -> Sender<ToCache>
 | 
			
		||||
    where
 | 
			
		||||
        P: Into<PathBuf>,
 | 
			
		||||
    {
 | 
			
		||||
        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<ToCache>) -> 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<P>(_dir: P) -> Result<MoreThanText, MTTError>
 | 
			
		||||
pub async fn start_db<P>(dir: P) -> Result<MoreThanText, MTTError>
 | 
			
		||||
where
 | 
			
		||||
    P: Into<PathBuf>,
 | 
			
		||||
{
 | 
			
		||||
    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))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user