Moved entry into separate file.
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
				
			|||||||
use super::{DBError, ErrorCode, FileData, SessionData, Store};
 | 
					use super::{DBError, FileData, SessionData};
 | 
				
			||||||
use std::slice;
 | 
					use std::slice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
@@ -16,21 +16,21 @@ impl FileData<Self> for Database {
 | 
				
			|||||||
        output
 | 
					        output
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn from_bytes(data: &mut slice::Iter<u8>) -> Result<Self, DBError> {
 | 
					    fn from_bytes(_data: &mut slice::Iter<u8>) -> Result<Self, DBError> {
 | 
				
			||||||
        Ok(Self {})
 | 
					        Ok(Self {})
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SessionData for Database {
 | 
					impl SessionData for Database {
 | 
				
			||||||
    fn add(&mut self, key: &str, value: &str, data: &str) -> Result<Vec<String>, DBError> {
 | 
					    fn add(&mut self, _key: &str, _value: &str, _data: &str) -> Result<Vec<String>, DBError> {
 | 
				
			||||||
        Ok(Vec::new())
 | 
					        Ok(Vec::new())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn eq(&self, key: &str, value: &str) -> Result<Vec<String>, DBError> {
 | 
					    fn eq(&self, _key: &str, _value: &str) -> Result<Vec<String>, DBError> {
 | 
				
			||||||
        Ok(Vec::new())
 | 
					        Ok(Vec::new())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn list(&self, keys: Vec<&str>) -> Result<Vec<String>, DBError> {
 | 
					    fn list(&self, _keys: Vec<&str>) -> Result<Vec<String>, DBError> {
 | 
				
			||||||
        Ok(Vec::new())
 | 
					        Ok(Vec::new())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										375
									
								
								src/morethantext/entry.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										375
									
								
								src/morethantext/entry.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,375 @@
 | 
				
			|||||||
 | 
					use async_std::{fs::{read, remove_file, write}, path::PathBuf};
 | 
				
			||||||
 | 
					use super::{DataType, DBError, ErrorCode, FileData, SessionData};
 | 
				
			||||||
 | 
					use std::{cell::Cell, time::{Duration, Instant}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Entry {
 | 
				
			||||||
 | 
					    data: DataType,
 | 
				
			||||||
 | 
					    filename: PathBuf,
 | 
				
			||||||
 | 
					    last_used: Cell<Instant>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Entry {
 | 
				
			||||||
 | 
					    pub async fn new<P>(filename: P, data: DataType) -> Result<Self, DBError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        P: Into<PathBuf>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let pathbuf = filename.into();
 | 
				
			||||||
 | 
					        if pathbuf.as_path().exists().await {
 | 
				
			||||||
 | 
					            return Err(DBError::from_code(ErrorCode::EntryExists(pathbuf)));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            match write(&pathbuf, data.to_bytes()).await {
 | 
				
			||||||
 | 
					                Ok(_) => (),
 | 
				
			||||||
 | 
					                Err(err) => {
 | 
				
			||||||
 | 
					                    let mut error = DBError::from_code(ErrorCode::EntryWriteFailure(pathbuf));
 | 
				
			||||||
 | 
					                    error.add_source(err);
 | 
				
			||||||
 | 
					                    return Err(error);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            data: data,
 | 
				
			||||||
 | 
					            filename: pathbuf,
 | 
				
			||||||
 | 
					            last_used: Cell::new(Instant::now()),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub async fn get<P>(filename: P) -> Result<Self, DBError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        P: Into<PathBuf>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let pathbuf = filename.into();
 | 
				
			||||||
 | 
					        let content = match read(&pathbuf).await {
 | 
				
			||||||
 | 
					            Ok(text) => text,
 | 
				
			||||||
 | 
					            Err(err) => {
 | 
				
			||||||
 | 
					                let mut error = DBError::from_code(ErrorCode::EntryReadFailure(pathbuf));
 | 
				
			||||||
 | 
					                error.add_source(err);
 | 
				
			||||||
 | 
					                return Err(error);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let data = match DataType::from_bytes(&mut content.iter()) {
 | 
				
			||||||
 | 
					            Ok(raw) => raw,
 | 
				
			||||||
 | 
					            Err(err) => {
 | 
				
			||||||
 | 
					                let mut error = DBError::from_code(ErrorCode::EntryReadFailure(pathbuf));
 | 
				
			||||||
 | 
					                error.add_source(err);
 | 
				
			||||||
 | 
					                return Err(error);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            data: data,
 | 
				
			||||||
 | 
					            filename: pathbuf,
 | 
				
			||||||
 | 
					            last_used: Cell::new(Instant::now()),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn elapsed(&self) -> Duration {
 | 
				
			||||||
 | 
					        self.last_used.get().elapsed()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn data(&self) -> DataType {
 | 
				
			||||||
 | 
					        self.last_used.set(Instant::now());
 | 
				
			||||||
 | 
					        self.data.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn update(&mut self, data: DataType) -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        self.last_used.set(Instant::now());
 | 
				
			||||||
 | 
					        match write(&self.filename, data.to_bytes()).await {
 | 
				
			||||||
 | 
					            Ok(_) => (),
 | 
				
			||||||
 | 
					            Err(err) => {
 | 
				
			||||||
 | 
					                let mut error =
 | 
				
			||||||
 | 
					                    DBError::from_code(ErrorCode::EntryWriteFailure(self.filename.clone()));
 | 
				
			||||||
 | 
					                error.add_source(err);
 | 
				
			||||||
 | 
					                return Err(error);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        self.data = data;
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn remove(&self) -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        match remove_file(&self.filename).await {
 | 
				
			||||||
 | 
					            Ok(_) => Ok(()),
 | 
				
			||||||
 | 
					            Err(err) => {
 | 
				
			||||||
 | 
					                let mut error =
 | 
				
			||||||
 | 
					                    DBError::from_code(ErrorCode::EntryDeleteFailure(self.filename.clone()));
 | 
				
			||||||
 | 
					                error.add_source(err);
 | 
				
			||||||
 | 
					                Err(error)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod entry {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    use std::error::Error;
 | 
				
			||||||
 | 
					    use tempfile::tempdir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn get_elapsed_time() {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("count");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let item = Entry::new(filename.to_string(), data).await.unwrap();
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            Duration::from_secs(1) > item.elapsed(),
 | 
				
			||||||
 | 
					            "last_used should have been now."
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        item.last_used
 | 
				
			||||||
 | 
					            .set(Instant::now() - Duration::from_secs(500));
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            Duration::from_secs(499) < item.elapsed(),
 | 
				
			||||||
 | 
					            "The duration should have increased."
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn create() {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let mut data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        data.add("database", "roger", "moore").unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("wiliam");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let item = Entry::new(filename.to_string(), data.clone())
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            Duration::from_secs(1) > item.elapsed(),
 | 
				
			||||||
 | 
					            "last_used should have been now."
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let output = item.data();
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            data.list(["database"].to_vec()).unwrap(),
 | 
				
			||||||
 | 
					            output.list(["database"].to_vec()).unwrap()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert!(filepath.is_file(), "Should have created the entry file.");
 | 
				
			||||||
 | 
					        let content = read(&filepath).await.unwrap();
 | 
				
			||||||
 | 
					        assert_eq!(content, data.to_bytes());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn create_errors_on_bad_files() -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("bad").join("path");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        match Entry::new(filename.to_string(), data).await {
 | 
				
			||||||
 | 
					            Ok(_) => Err(DBError::new("bad file names should raise an error")),
 | 
				
			||||||
 | 
					            Err(err) => match err.code {
 | 
				
			||||||
 | 
					                ErrorCode::EntryWriteFailure(_) => {
 | 
				
			||||||
 | 
					                    assert!(err.source().is_some(), "Must include the source error.");
 | 
				
			||||||
 | 
					                    assert!(err
 | 
				
			||||||
 | 
					                        .source()
 | 
				
			||||||
 | 
					                        .unwrap()
 | 
				
			||||||
 | 
					                        .to_string()
 | 
				
			||||||
 | 
					                        .contains("could not write to file"));
 | 
				
			||||||
 | 
					                    Ok(())
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => Err(DBError::new("incorrect error code")),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn create_does_not_over_writes() -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let id = "wicked";
 | 
				
			||||||
 | 
					        let file = dir.path().join(id);
 | 
				
			||||||
 | 
					        let filename = file.to_str().unwrap();
 | 
				
			||||||
 | 
					        write(&file, b"previous").await.unwrap();
 | 
				
			||||||
 | 
					        let data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        match Entry::new(filename.to_string(), data).await {
 | 
				
			||||||
 | 
					            Ok(_) => {
 | 
				
			||||||
 | 
					                return Err(DBError::new(
 | 
				
			||||||
 | 
					                    "Should produce an error for an existing Entry",
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Err(err) => match err.code {
 | 
				
			||||||
 | 
					                ErrorCode::EntryExists(_) => Ok(()),
 | 
				
			||||||
 | 
					                _ => Err(DBError::new("incorrect error code")),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn get_updates_last_used() {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("holder");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let item = Entry::new(filename.to_string(), data).await.unwrap();
 | 
				
			||||||
 | 
					        item.last_used
 | 
				
			||||||
 | 
					            .set(Instant::now() - Duration::from_secs(300));
 | 
				
			||||||
 | 
					        item.data();
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            Duration::from_secs(1) > item.elapsed(),
 | 
				
			||||||
 | 
					            "last_used should have been reset."
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn update_entry() {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let mut data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("changing");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let mut item = Entry::new(filename.to_string(), data.clone())
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        item.last_used
 | 
				
			||||||
 | 
					            .set(Instant::now() - Duration::from_secs(500));
 | 
				
			||||||
 | 
					        data.add("database", "new", "stuff").unwrap();
 | 
				
			||||||
 | 
					        item.update(data.clone()).await.unwrap();
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            Duration::from_secs(1) > item.elapsed(),
 | 
				
			||||||
 | 
					            "last_used should have been reset."
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let output = item.data();
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            data.list(["database"].to_vec()).unwrap(),
 | 
				
			||||||
 | 
					            output.list(["database"].to_vec()).unwrap()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let content = read(&filepath).await.unwrap();
 | 
				
			||||||
 | 
					        assert_eq!(content, data.to_bytes());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn update_write_errors() -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("changing");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let mut item = Entry::new(filename.to_string(), data.clone())
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        drop(dir);
 | 
				
			||||||
 | 
					        match item.update(data).await {
 | 
				
			||||||
 | 
					            Ok(_) => Err(DBError::new("file writes should return an error")),
 | 
				
			||||||
 | 
					            Err(err) => match err.code {
 | 
				
			||||||
 | 
					                ErrorCode::EntryWriteFailure(_) => {
 | 
				
			||||||
 | 
					                    assert!(err.source().is_some(), "Must include the source error.");
 | 
				
			||||||
 | 
					                    assert!(err
 | 
				
			||||||
 | 
					                        .source()
 | 
				
			||||||
 | 
					                        .unwrap()
 | 
				
			||||||
 | 
					                        .to_string()
 | 
				
			||||||
 | 
					                        .contains("could not write to file"));
 | 
				
			||||||
 | 
					                    Ok(())
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => Err(DBError::new("incorrect error code")),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn retrieve() {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let mut data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        data.add("database", "something_old", "3.14159").unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("existing");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let item = Entry::new(filename.to_string(), data.clone())
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        let output = Entry::get(filename).await.unwrap();
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            output.data().list(["database"].to_vec()).unwrap(),
 | 
				
			||||||
 | 
					            data.list(["database"].to_vec()).unwrap()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(output.filename.to_str().unwrap(), filename);
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            Duration::from_secs(1) > item.elapsed(),
 | 
				
			||||||
 | 
					            "last_used should have been reset."
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn retrieve_file_missing() -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("justnotthere");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        match Entry::get(filename).await {
 | 
				
			||||||
 | 
					            Ok(_) => Err(DBError::new("should have returned an error")),
 | 
				
			||||||
 | 
					            Err(err) => match err.code {
 | 
				
			||||||
 | 
					                ErrorCode::EntryReadFailure(_) => {
 | 
				
			||||||
 | 
					                    assert!(err.source().is_some(), "Error should have a source.");
 | 
				
			||||||
 | 
					                    assert!(
 | 
				
			||||||
 | 
					                        err.source()
 | 
				
			||||||
 | 
					                            .unwrap()
 | 
				
			||||||
 | 
					                            .to_string()
 | 
				
			||||||
 | 
					                            .contains("could not read file"),
 | 
				
			||||||
 | 
					                        "Source Error Message: {}",
 | 
				
			||||||
 | 
					                        err.source().unwrap().to_string()
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    Ok(())
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => Err(DBError::new("incorrect error code")),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn retrieve_corrupt_file() -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("garbage");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        write(&filepath, b"jhsdfghlsdf").await.unwrap();
 | 
				
			||||||
 | 
					        match Entry::get(filename).await {
 | 
				
			||||||
 | 
					            Ok(_) => Err(DBError::new("should have returned an error")),
 | 
				
			||||||
 | 
					            Err(err) => match err.code {
 | 
				
			||||||
 | 
					                ErrorCode::EntryReadFailure(_) => {
 | 
				
			||||||
 | 
					                    assert!(err.source().is_some(), "Error should have a source.");
 | 
				
			||||||
 | 
					                    assert!(
 | 
				
			||||||
 | 
					                        err.source().unwrap().to_string().contains("corrupt file"),
 | 
				
			||||||
 | 
					                        "Source Error Message: {}",
 | 
				
			||||||
 | 
					                        err.source().unwrap().to_string()
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    Ok(())
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => Err(DBError::new("incorrect error code")),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn delete() {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("byebye");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        let item = Entry::new(filename.to_string(), data.clone())
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        item.remove().await.unwrap();
 | 
				
			||||||
 | 
					        assert!(!filepath.exists(), "Entry file should be removed.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[async_std::test]
 | 
				
			||||||
 | 
					    async fn delete_bad_file() -> Result<(), DBError> {
 | 
				
			||||||
 | 
					        let dir = tempdir().unwrap();
 | 
				
			||||||
 | 
					        let filepath = dir.path().join("itsnotthere");
 | 
				
			||||||
 | 
					        let filename = filepath.to_str().unwrap();
 | 
				
			||||||
 | 
					        let data = DataType::new("store").unwrap();
 | 
				
			||||||
 | 
					        let item = Entry::new(filename.to_string(), data.clone())
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        remove_file(filename).await.unwrap();
 | 
				
			||||||
 | 
					        match item.remove().await {
 | 
				
			||||||
 | 
					            Ok(_) => Err(DBError::new("should have produced an error")),
 | 
				
			||||||
 | 
					            Err(err) => match err.code {
 | 
				
			||||||
 | 
					                ErrorCode::EntryDeleteFailure(_) => {
 | 
				
			||||||
 | 
					                    assert!(err.source().is_some(), "Error should have a source.");
 | 
				
			||||||
 | 
					                    assert!(
 | 
				
			||||||
 | 
					                        err.source()
 | 
				
			||||||
 | 
					                            .unwrap()
 | 
				
			||||||
 | 
					                            .to_string()
 | 
				
			||||||
 | 
					                            .contains("could not remove file"),
 | 
				
			||||||
 | 
					                        "Source Error Message: {}",
 | 
				
			||||||
 | 
					                        err.source().unwrap().to_string()
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    Ok(())
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => Err(DBError::new("incorrect error code")),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -158,11 +158,11 @@ mod codes {
 | 
				
			|||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    use async_std::path::PathBuf;
 | 
					    use async_std::path::PathBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const items: [&str; 2] = ["first", "second"];
 | 
					    const ITEMS: [&str; 2] = ["first", "second"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn create_path_buffer() -> Vec<PathBuf> {
 | 
					    fn create_path_buffer() -> Vec<PathBuf> {
 | 
				
			||||||
        let mut output = Vec::new();
 | 
					        let mut output = Vec::new();
 | 
				
			||||||
        for item in items {
 | 
					        for item in ITEMS {
 | 
				
			||||||
            let mut path = PathBuf::new();
 | 
					            let mut path = PathBuf::new();
 | 
				
			||||||
            path.push("thepath");
 | 
					            path.push("thepath");
 | 
				
			||||||
            path.push(item);
 | 
					            path.push(item);
 | 
				
			||||||
@@ -173,7 +173,7 @@ mod codes {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn undefined_display() {
 | 
					    fn undefined_display() {
 | 
				
			||||||
        for item in items {
 | 
					        for item in ITEMS {
 | 
				
			||||||
            let err = ErrorCode::Undefined(item.to_string());
 | 
					            let err = ErrorCode::Undefined(item.to_string());
 | 
				
			||||||
            assert_eq!(err.to_string(), item);
 | 
					            assert_eq!(err.to_string(), item);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -181,7 +181,7 @@ mod codes {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn incorrect_data_type() {
 | 
					    fn incorrect_data_type() {
 | 
				
			||||||
        for item in items {
 | 
					        for item in ITEMS {
 | 
				
			||||||
            let err = ErrorCode::DataTypeIncorrect(item.to_string());
 | 
					            let err = ErrorCode::DataTypeIncorrect(item.to_string());
 | 
				
			||||||
            assert_eq!(
 | 
					            assert_eq!(
 | 
				
			||||||
                err.to_string(),
 | 
					                err.to_string(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,16 @@
 | 
				
			|||||||
mod database;
 | 
					mod database;
 | 
				
			||||||
 | 
					mod entry;
 | 
				
			||||||
mod error;
 | 
					mod error;
 | 
				
			||||||
mod store;
 | 
					mod store;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use async_std::{
 | 
					use async_std::{
 | 
				
			||||||
    fs::{read, remove_file, write},
 | 
					    fs::{write},
 | 
				
			||||||
    path::{Path, PathBuf},
 | 
					    path::PathBuf,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use database::Database;
 | 
					use database::Database;
 | 
				
			||||||
 | 
					use entry::Entry;
 | 
				
			||||||
use error::{DBError, ErrorCode};
 | 
					use error::{DBError, ErrorCode};
 | 
				
			||||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
 | 
					use std::{slice, str};
 | 
				
			||||||
use std::{
 | 
					 | 
				
			||||||
    cell::Cell,
 | 
					 | 
				
			||||||
    slice, str,
 | 
					 | 
				
			||||||
    time::{Duration, Instant},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use store::Store;
 | 
					use store::Store;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ENTRY: &str = "EntryPoint";
 | 
					const ENTRY: &str = "EntryPoint";
 | 
				
			||||||
@@ -30,7 +27,7 @@ trait SessionData {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
enum DataType {
 | 
					pub enum DataType {
 | 
				
			||||||
    DBMap(Store),
 | 
					    DBMap(Store),
 | 
				
			||||||
    TableMap(Database),
 | 
					    TableMap(Database),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -80,7 +77,7 @@ impl FileData<Self> for DataType {
 | 
				
			|||||||
        output.push(0);
 | 
					        output.push(0);
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            DataType::DBMap(store) => output.append(&mut store.to_bytes()),
 | 
					            DataType::DBMap(store) => output.append(&mut store.to_bytes()),
 | 
				
			||||||
            DataType::TableMap(db) => (),
 | 
					            DataType::TableMap(_) => (),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        output
 | 
					        output
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -113,102 +110,6 @@ impl FileData<Self> for DataType {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Entry {
 | 
					 | 
				
			||||||
    data: DataType,
 | 
					 | 
				
			||||||
    filename: PathBuf,
 | 
					 | 
				
			||||||
    last_used: Cell<Instant>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Entry {
 | 
					 | 
				
			||||||
    async fn new<P>(filename: P, data: DataType) -> Result<Self, DBError>
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        P: Into<PathBuf>,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let pathbuf = filename.into();
 | 
					 | 
				
			||||||
        if pathbuf.as_path().exists().await {
 | 
					 | 
				
			||||||
            return Err(DBError::from_code(ErrorCode::EntryExists(pathbuf)));
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            match write(&pathbuf, data.to_bytes()).await {
 | 
					 | 
				
			||||||
                Ok(_) => (),
 | 
					 | 
				
			||||||
                Err(err) => {
 | 
					 | 
				
			||||||
                    let mut error = DBError::from_code(ErrorCode::EntryWriteFailure(pathbuf));
 | 
					 | 
				
			||||||
                    error.add_source(err);
 | 
					 | 
				
			||||||
                    return Err(error);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            data: data,
 | 
					 | 
				
			||||||
            filename: pathbuf,
 | 
					 | 
				
			||||||
            last_used: Cell::new(Instant::now()),
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn get<P>(filename: P) -> Result<Self, DBError>
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        P: Into<PathBuf>,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let pathbuf = filename.into();
 | 
					 | 
				
			||||||
        let content = match read(&pathbuf).await {
 | 
					 | 
				
			||||||
            Ok(text) => text,
 | 
					 | 
				
			||||||
            Err(err) => {
 | 
					 | 
				
			||||||
                let mut error = DBError::from_code(ErrorCode::EntryReadFailure(pathbuf));
 | 
					 | 
				
			||||||
                error.add_source(err);
 | 
					 | 
				
			||||||
                return Err(error);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        let data = match DataType::from_bytes(&mut content.iter()) {
 | 
					 | 
				
			||||||
            Ok(raw) => raw,
 | 
					 | 
				
			||||||
            Err(err) => {
 | 
					 | 
				
			||||||
                let mut error = DBError::from_code(ErrorCode::EntryReadFailure(pathbuf));
 | 
					 | 
				
			||||||
                error.add_source(err);
 | 
					 | 
				
			||||||
                return Err(error);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            data: data,
 | 
					 | 
				
			||||||
            filename: pathbuf,
 | 
					 | 
				
			||||||
            last_used: Cell::new(Instant::now()),
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn elapsed(&self) -> Duration {
 | 
					 | 
				
			||||||
        self.last_used.get().elapsed()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn data(&self) -> DataType {
 | 
					 | 
				
			||||||
        self.last_used.set(Instant::now());
 | 
					 | 
				
			||||||
        self.data.clone()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn update(&mut self, data: DataType) -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        self.last_used.set(Instant::now());
 | 
					 | 
				
			||||||
        match write(&self.filename, data.to_bytes()).await {
 | 
					 | 
				
			||||||
            Ok(_) => (),
 | 
					 | 
				
			||||||
            Err(err) => {
 | 
					 | 
				
			||||||
                let mut error =
 | 
					 | 
				
			||||||
                    DBError::from_code(ErrorCode::EntryWriteFailure(self.filename.clone()));
 | 
					 | 
				
			||||||
                error.add_source(err);
 | 
					 | 
				
			||||||
                return Err(error);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        self.data = data;
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn remove(&self) -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        match remove_file(&self.filename).await {
 | 
					 | 
				
			||||||
            Ok(_) => Ok(()),
 | 
					 | 
				
			||||||
            Err(err) => {
 | 
					 | 
				
			||||||
                let mut error =
 | 
					 | 
				
			||||||
                    DBError::from_code(ErrorCode::EntryDeleteFailure(self.filename.clone()));
 | 
					 | 
				
			||||||
                error.add_source(err);
 | 
					 | 
				
			||||||
                Err(error)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
pub struct MoreThanText;
 | 
					pub struct MoreThanText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -395,282 +296,6 @@ mod datatype_file {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					 | 
				
			||||||
mod entry {
 | 
					 | 
				
			||||||
    use super::*;
 | 
					 | 
				
			||||||
    use std::error::Error;
 | 
					 | 
				
			||||||
    use tempfile::tempdir;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn get_elapsed_time() {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("count");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let item = Entry::new(filename.to_string(), data).await.unwrap();
 | 
					 | 
				
			||||||
        assert!(
 | 
					 | 
				
			||||||
            Duration::from_secs(1) > item.elapsed(),
 | 
					 | 
				
			||||||
            "last_used should have been now."
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        item.last_used
 | 
					 | 
				
			||||||
            .set(Instant::now() - Duration::from_secs(500));
 | 
					 | 
				
			||||||
        assert!(
 | 
					 | 
				
			||||||
            Duration::from_secs(499) < item.elapsed(),
 | 
					 | 
				
			||||||
            "The duration should have increased."
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn create() {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let mut data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        data.add("database", "roger", "moore").unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("wiliam");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let item = Entry::new(filename.to_string(), data.clone())
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        assert!(
 | 
					 | 
				
			||||||
            Duration::from_secs(1) > item.elapsed(),
 | 
					 | 
				
			||||||
            "last_used should have been now."
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        let output = item.data();
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            data.list(["database"].to_vec()).unwrap(),
 | 
					 | 
				
			||||||
            output.list(["database"].to_vec()).unwrap()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        assert!(filepath.is_file(), "Should have created the entry file.");
 | 
					 | 
				
			||||||
        let content = read(&filepath).await.unwrap();
 | 
					 | 
				
			||||||
        assert_eq!(content, data.to_bytes());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn create_errors_on_bad_files() -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("bad").join("path");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        match Entry::new(filename.to_string(), data).await {
 | 
					 | 
				
			||||||
            Ok(_) => Err(DBError::new("bad file names should raise an error")),
 | 
					 | 
				
			||||||
            Err(err) => match err.code {
 | 
					 | 
				
			||||||
                ErrorCode::EntryWriteFailure(_) => {
 | 
					 | 
				
			||||||
                    assert!(err.source().is_some(), "Must include the source error.");
 | 
					 | 
				
			||||||
                    assert!(err
 | 
					 | 
				
			||||||
                        .source()
 | 
					 | 
				
			||||||
                        .unwrap()
 | 
					 | 
				
			||||||
                        .to_string()
 | 
					 | 
				
			||||||
                        .contains("could not write to file"));
 | 
					 | 
				
			||||||
                    Ok(())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _ => Err(DBError::new("incorrect error code")),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn create_does_not_over_writes() -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let id = "wicked";
 | 
					 | 
				
			||||||
        let file = dir.path().join(id);
 | 
					 | 
				
			||||||
        let filename = file.to_str().unwrap();
 | 
					 | 
				
			||||||
        write(&file, b"previous").await.unwrap();
 | 
					 | 
				
			||||||
        let data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        match Entry::new(filename.to_string(), data).await {
 | 
					 | 
				
			||||||
            Ok(_) => {
 | 
					 | 
				
			||||||
                return Err(DBError::new(
 | 
					 | 
				
			||||||
                    "Should produce an error for an existing Entry",
 | 
					 | 
				
			||||||
                ))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Err(err) => match err.code {
 | 
					 | 
				
			||||||
                ErrorCode::EntryExists(_) => Ok(()),
 | 
					 | 
				
			||||||
                _ => Err(DBError::new("incorrect error code")),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn get_updates_last_used() {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("holder");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let item = Entry::new(filename.to_string(), data).await.unwrap();
 | 
					 | 
				
			||||||
        item.last_used
 | 
					 | 
				
			||||||
            .set(Instant::now() - Duration::from_secs(300));
 | 
					 | 
				
			||||||
        item.data();
 | 
					 | 
				
			||||||
        assert!(
 | 
					 | 
				
			||||||
            Duration::from_secs(1) > item.elapsed(),
 | 
					 | 
				
			||||||
            "last_used should have been reset."
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn update_entry() {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let mut data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("changing");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let mut item = Entry::new(filename.to_string(), data.clone())
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        item.last_used
 | 
					 | 
				
			||||||
            .set(Instant::now() - Duration::from_secs(500));
 | 
					 | 
				
			||||||
        data.add("database", "new", "stuff").unwrap();
 | 
					 | 
				
			||||||
        item.update(data.clone()).await.unwrap();
 | 
					 | 
				
			||||||
        assert!(
 | 
					 | 
				
			||||||
            Duration::from_secs(1) > item.elapsed(),
 | 
					 | 
				
			||||||
            "last_used should have been reset."
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        let output = item.data();
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            data.list(["database"].to_vec()).unwrap(),
 | 
					 | 
				
			||||||
            output.list(["database"].to_vec()).unwrap()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        let content = read(&filepath).await.unwrap();
 | 
					 | 
				
			||||||
        assert_eq!(content, data.to_bytes());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn update_write_errors() -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("changing");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let mut item = Entry::new(filename.to_string(), data.clone())
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        drop(dir);
 | 
					 | 
				
			||||||
        match item.update(data).await {
 | 
					 | 
				
			||||||
            Ok(_) => Err(DBError::new("file writes should return an error")),
 | 
					 | 
				
			||||||
            Err(err) => match err.code {
 | 
					 | 
				
			||||||
                ErrorCode::EntryWriteFailure(_) => {
 | 
					 | 
				
			||||||
                    assert!(err.source().is_some(), "Must include the source error.");
 | 
					 | 
				
			||||||
                    assert!(err
 | 
					 | 
				
			||||||
                        .source()
 | 
					 | 
				
			||||||
                        .unwrap()
 | 
					 | 
				
			||||||
                        .to_string()
 | 
					 | 
				
			||||||
                        .contains("could not write to file"));
 | 
					 | 
				
			||||||
                    Ok(())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _ => Err(DBError::new("incorrect error code")),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn retrieve() {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let mut data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        data.add("database", "something_old", "3.14159").unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("existing");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let item = Entry::new(filename.to_string(), data.clone())
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        let output = Entry::get(filename).await.unwrap();
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            output.data().list(["database"].to_vec()).unwrap(),
 | 
					 | 
				
			||||||
            data.list(["database"].to_vec()).unwrap()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        assert_eq!(output.filename.to_str().unwrap(), filename);
 | 
					 | 
				
			||||||
        assert!(
 | 
					 | 
				
			||||||
            Duration::from_secs(1) > item.elapsed(),
 | 
					 | 
				
			||||||
            "last_used should have been reset."
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn retrieve_file_missing() -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("justnotthere");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        match Entry::get(filename).await {
 | 
					 | 
				
			||||||
            Ok(_) => Err(DBError::new("should have returned an error")),
 | 
					 | 
				
			||||||
            Err(err) => match err.code {
 | 
					 | 
				
			||||||
                ErrorCode::EntryReadFailure(_) => {
 | 
					 | 
				
			||||||
                    assert!(err.source().is_some(), "Error should have a source.");
 | 
					 | 
				
			||||||
                    assert!(
 | 
					 | 
				
			||||||
                        err.source()
 | 
					 | 
				
			||||||
                            .unwrap()
 | 
					 | 
				
			||||||
                            .to_string()
 | 
					 | 
				
			||||||
                            .contains("could not read file"),
 | 
					 | 
				
			||||||
                        "Source Error Message: {}",
 | 
					 | 
				
			||||||
                        err.source().unwrap().to_string()
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                    Ok(())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _ => Err(DBError::new("incorrect error code")),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn retrieve_corrupt_file() -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("garbage");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        write(&filepath, b"jhsdfghlsdf").await.unwrap();
 | 
					 | 
				
			||||||
        match Entry::get(filename).await {
 | 
					 | 
				
			||||||
            Ok(_) => Err(DBError::new("should have returned an error")),
 | 
					 | 
				
			||||||
            Err(err) => match err.code {
 | 
					 | 
				
			||||||
                ErrorCode::EntryReadFailure(_) => {
 | 
					 | 
				
			||||||
                    assert!(err.source().is_some(), "Error should have a source.");
 | 
					 | 
				
			||||||
                    assert!(
 | 
					 | 
				
			||||||
                        err.source().unwrap().to_string().contains("corrupt file"),
 | 
					 | 
				
			||||||
                        "Source Error Message: {}",
 | 
					 | 
				
			||||||
                        err.source().unwrap().to_string()
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                    Ok(())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _ => Err(DBError::new("incorrect error code")),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn delete() {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("byebye");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        let item = Entry::new(filename.to_string(), data.clone())
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        item.remove().await.unwrap();
 | 
					 | 
				
			||||||
        assert!(!filepath.exists(), "Entry file should be removed.");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[async_std::test]
 | 
					 | 
				
			||||||
    async fn delete_bad_file() -> Result<(), DBError> {
 | 
					 | 
				
			||||||
        let dir = tempdir().unwrap();
 | 
					 | 
				
			||||||
        let filepath = dir.path().join("itsnotthere");
 | 
					 | 
				
			||||||
        let filename = filepath.to_str().unwrap();
 | 
					 | 
				
			||||||
        let data = DataType::new("store").unwrap();
 | 
					 | 
				
			||||||
        let item = Entry::new(filename.to_string(), data.clone())
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        remove_file(filename).await.unwrap();
 | 
					 | 
				
			||||||
        match item.remove().await {
 | 
					 | 
				
			||||||
            Ok(_) => Err(DBError::new("should have produced an error")),
 | 
					 | 
				
			||||||
            Err(err) => match err.code {
 | 
					 | 
				
			||||||
                ErrorCode::EntryDeleteFailure(_) => {
 | 
					 | 
				
			||||||
                    assert!(err.source().is_some(), "Error should have a source.");
 | 
					 | 
				
			||||||
                    assert!(
 | 
					 | 
				
			||||||
                        err.source()
 | 
					 | 
				
			||||||
                            .unwrap()
 | 
					 | 
				
			||||||
                            .to_string()
 | 
					 | 
				
			||||||
                            .contains("could not remove file"),
 | 
					 | 
				
			||||||
                        "Source Error Message: {}",
 | 
					 | 
				
			||||||
                        err.source().unwrap().to_string()
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                    Ok(())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _ => Err(DBError::new("incorrect error code")),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod cache {
 | 
					mod cache {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
@@ -724,7 +349,7 @@ mod cache {
 | 
				
			|||||||
        Entry::new(dir.path().join(ENTRY), data.clone())
 | 
					        Entry::new(dir.path().join(ENTRY), data.clone())
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
        let cache = MoreThanText::new(dir.path()).await.unwrap();
 | 
					        MoreThanText::new(dir.path()).await.unwrap();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[async_std::test]
 | 
					    #[async_std::test]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,6 @@ impl FileData<Self> for Store {
 | 
				
			|||||||
        let mut id: Vec<u8> = Vec::new();
 | 
					        let mut id: Vec<u8> = Vec::new();
 | 
				
			||||||
        let mut get_id = false;
 | 
					        let mut get_id = false;
 | 
				
			||||||
        let mut letter: u8;
 | 
					        let mut letter: u8;
 | 
				
			||||||
        let err_msg = "file corruption";
 | 
					 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            match data.next() {
 | 
					            match data.next() {
 | 
				
			||||||
                Some(a) => letter = a.clone(),
 | 
					                Some(a) => letter = a.clone(),
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user