From e2eee6beb1a0f3c732140dc09bcc245abde6109e Mon Sep 17 00:00:00 2001 From: Avril Date: Tue, 22 Dec 2020 19:08:58 +0000 Subject: [PATCH] creation, insertion of entries --- Cargo.lock | 24 +++++++++ Cargo.toml | 1 + src/data/entry.rs | 35 ++++++++++--- src/data/entry/builder.rs | 107 ++++++++++++++++++++++++++++++++++++++ src/data/mod.rs | 19 ++----- src/data/mutation.rs | 3 +- src/data/test.rs | 61 ++++++++++++++++++++++ 7 files changed, 229 insertions(+), 21 deletions(-) create mode 100644 src/data/entry/builder.rs create mode 100644 src/data/test.rs diff --git a/Cargo.lock b/Cargo.lock index 9230587..33897df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,6 +325,12 @@ dependencies = [ "libc", ] +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + [[package]] name = "jemalloc-sys" version = "0.3.2" @@ -470,6 +476,7 @@ dependencies = [ "memmap", "pin-project", "serde", + "serde_json", "smallvec", "tokio", ] @@ -662,6 +669,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + [[package]] name = "serde" version = "1.0.118" @@ -682,6 +695,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.9.2" diff --git a/Cargo.toml b/Cargo.toml index 531a199..32b639f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,5 +17,6 @@ jemallocator = "0.3.2" memmap = "0.7.0" pin-project = "1.0.2" serde = {version = "1.0.118", features= ["derive"]} +serde_json = "1.0.60" smallvec = "1.5.1" tokio = {version = "0.2", features = ["full"]} diff --git a/src/data/entry.rs b/src/data/entry.rs index 60759a8..3a5e620 100644 --- a/src/data/entry.rs +++ b/src/data/entry.rs @@ -2,6 +2,7 @@ use super::*; use std::hash::{Hash, Hasher}; use std::borrow::Borrow; +pub mod builder; #[derive(Debug, Serialize, Deserialize)] // Clone, PartialEq, Eq, Hash pub struct Entry @@ -32,16 +33,38 @@ pub struct Entry cache: DataCacheState, } +// Accessors impl Entry { - pub(super) fn purge_from_host(&self, host: &mut Store) + /// The name of this entry + pub fn name(&self) -> &str { - host.tag_search_any(&self.tags[..]).dedup_ref(); //TODO + &self.name + } + pub fn description(&self) -> &str + { + &self.description + } + /// The *original* filename of this entry + pub fn filename(&self) -> &Path + { + Path::new(&self.filename) + } + /// The path of this entry's file relative to the root of its store + pub fn location(&self) -> &Path + { + self.location.as_path() + } + /// The tags for this entry + pub fn tags(&self) -> &[String] + { + &self.tags[..] + } + /// Is the file of this entry encrypted? + pub fn is_encrypted(&self) -> bool + { + self.key.is_some() } -} - -impl Entry -{ /// The sha256 hash of the data in this entry pub fn hash(&self) -> &sha256::Sha256Hash { diff --git a/src/data/entry/builder.rs b/src/data/entry/builder.rs new file mode 100644 index 0000000..f7ef26a --- /dev/null +++ b/src/data/entry/builder.rs @@ -0,0 +1,107 @@ +use super::*; +use std::{ + error, + fmt, +}; +use std::ffi::OsStr; + +/// Builder for creating [`Entry`] instances. +#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)] +pub struct EntryBuilder<'a> +{ + name: Option<&'a str>, + desc: Option<&'a str>, + + tags: Option<&'a[&'a str]>, + + key: Option, + hash: Option, + + location: Option<&'a Path>, + filename: Option<&'a OsStr>, +} + +macro_rules! build_fn { + ($name:ident: $ty:ty $(, $com:literal)?) => { + $(#[doc=$com])? + #[inline] pub const fn $name(self, $name: $ty) -> Self + { + Self { + $name: Some($name), + ..self + } + } + }; +} + +impl<'a> EntryBuilder<'a> +{ + /// Create a new entry builder + #[inline] pub const fn new() -> Self + { + macro_rules! default { + ($($name:ident),*) => { + Self { + $( + $name: None + ),* + } + } + } + default!{ + name, desc, tags, key, hash, location, filename + } + } + + build_fn!(name: &'a str, "Insert the name of this entry"); + build_fn!(desc: &'a str, "Insert a description for this entry"); + build_fn!(tags: &'a [&'a str], "Insert the tags for this entry"); + build_fn!(key: aes::AesKey, "Insert the encryption key for this entry"); + build_fn!(hash: sha256::Sha256Hash, "Insert the hash for this entry"); + build_fn!(location: &'a Path, "Insert the location for this entry"); + build_fn!(filename: &'a OsStr, "Insert the original filename for this entry"); + + /// Try to build an `Entry` from this builder. + #[inline] pub fn build(self) -> Result + { + macro_rules! fail_on { + ($name:ident) => { + self.$name.ok_or(EntryBuildError)? + }; + ($name:ident: $or:expr) =>{ + if let Some(name) = self.$name { + name + } else { + $or + } + }; + } + Ok( + Entry { + name: fail_on!(name).into(), + description: fail_on!(desc: "").into(), + tags: fail_on!(tags: &[]).iter().map(|&x| x.to_owned()).collect(), + key: self.key, + hash: fail_on!(hash), + location: fail_on!(location).into(), + filename: fail_on!(filename: OsStr::new("")).into(), + + cache: Default::default(), + } + ) + } +} + +/// Error for when an improporly configured [`EntryBuilder`] called `.build()`. +/// Usually this is because of missing fields. +#[derive(Debug)] +pub struct EntryBuildError; + +impl error::Error for EntryBuildError{} +impl fmt::Display for EntryBuildError +{ + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "improporly configured builder") + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs index ab57413..499ff7a 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -15,8 +15,10 @@ use memmap::Mmap; use bytes::Bytes; use cryptohelpers::{aes, sha256}; + mod entry; pub use entry::Entry; +pub use entry::builder::EntryBuilder; mod cache; pub use cache::DataCacheState; @@ -33,6 +35,8 @@ pub use search::*; mod mutation; pub use mutation::*; +#[cfg(test)] mod test; + /// The key used to look up a single entry in `O(1)` time. /// /// # Notes @@ -43,7 +47,7 @@ pub type EntryKey = sha256::Sha256Hash; /// /// # Notes /// Change this back to `RandomState` if you change the type of `EntryKey`. -pub type BuildHasher = Sha256TopBuildHasher; +pub type BuildHasher = std::collections::hash_map::RandomState;//Sha256TopBuildHasher; <-- bug here #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] struct PurgeTrack @@ -88,19 +92,6 @@ pub struct Store tags: BTreeMap, // string (tags) -> index (tag_mappings) -> index (data_hashes) -> hash used for lookup (data) } -// Comptime asserts (TODO: Unneeded. Remove.) -/*impl Store -{ - fn _assert_tag_lookup_works(&self, tag: &str) -> &Entry - { - self.data.get(self.data_hashes.get(*self.tag_mappings.get(*self.tags.get(tag).unwrap()).unwrap().first().unwrap()).unwrap()).unwrap() // y-yes this also works... - } - fn _assert_lookup_internal_works(&self, data_idx: ArenaIndex) -> &Entry - { - self.data.get(self.data_hashes.get(data_idx).unwrap()).unwrap() // yes this works.. - } -}*/ - // Creating impl Store { diff --git a/src/data/mutation.rs b/src/data/mutation.rs index 7ca58c6..4dc12fb 100644 --- a/src/data/mutation.rs +++ b/src/data/mutation.rs @@ -18,7 +18,8 @@ impl Store } /// Insert this entry then return a reference to it. - pub fn insert(&mut self, ent: Entry) -> &Entry + pub fn insert<'a, 'b>(&'a mut self, ent: Entry) -> &'b Entry + where 'a: 'b { let ffd = *ent.hash(); self.insert_overwrite(ent); diff --git a/src/data/test.rs b/src/data/test.rs new file mode 100644 index 0000000..f5f3875 --- /dev/null +++ b/src/data/test.rs @@ -0,0 +1,61 @@ +use super::*; + +#[inline] fn gen_meta() -> StoreMetadata +{ + StoreMetadata{ + name: "test".to_string(), + root: "./test/db".to_owned().into(), + } +} + +#[test] +fn create() +{ + let store = Store::new(gen_meta()); + let freeze = store.into_freeze(); + let _ = freeze.into_new(); +} + +#[test] +fn entries() +{ + let mut store = Store::new(gen_meta()); + macro_rules! build { + () => { + EntryBuilder::new() + .name("Hello") + .desc("Desc") + .tags(&["Tag1", "Tag2", "Tag3"]) + .hash(sha256::compute_slice(b"hello world")) + .location(Path::new("test_file")) + }; + } + let entry = build!() + .build().expect("Failed to build entry"); + + println!("Entry: {:#?}", entry); + let entry = store.insert(entry).clone(); + store.insert(build!() + .hash(sha256::compute_slice("something else")) + .name("Entry 2") + .build().expect("Failed to build entry 2")); + println!("Entry ref: {:#?}", entry); + println!("Store: {:#?}", store); + + let freeze = store.freeze(); + println!("Frozen: {:#?}", freeze); + let store2 = Store::unfreeze(&freeze); + + + assert_eq!(store2.tag_search("Tag2").filter(|x| x.name() == entry.name()).next().unwrap().hash(), entry.hash()); + assert_eq!(store.tag_search_any(vec!["Tag2", "Tag3"]).filter(|x| x.name() == entry.name()).next().unwrap().hash(), entry.hash()); + assert_eq!(store2.tag_search_all(vec!["Tag1", "Tag3"]).filter(|x| x.name() == entry.name()).next().unwrap().hash(), entry.hash()); + + assert!(store.tag_search_all(vec!["Tag1", "Nope"]).next().is_none()); + + let json = serde_json::to_string_pretty(&freeze).unwrap(); + println!("Freeze serialised ({} bytes): '{}'", json.len(), json); + let ufreeze: Freeze = serde_json::from_str(json.as_str()).unwrap(); + assert_eq!(ufreeze, freeze); + println!("Freeze unseralised as store: {:#?}", ufreeze.into_new()); +}