parent
2511939f4e
commit
e2eee6beb1
@ -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<aes::AesKey>,
|
||||||
|
hash: Option<sha256::Sha256Hash>,
|
||||||
|
|
||||||
|
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<Entry, EntryBuildError>
|
||||||
|
{
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
@ -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());
|
||||||
|
}
|
Loading…
Reference in new issue