2016-10-01 15:35:06 +00:00
|
|
|
//
|
|
|
|
// imag - the personal information management suite for the commandline
|
2019-01-03 01:32:07 +00:00
|
|
|
// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> and contributors
|
2016-10-01 15:35:06 +00:00
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; version
|
|
|
|
// 2.1 of the License.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
// License along with this library; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
//
|
|
|
|
|
2018-03-18 16:52:41 +00:00
|
|
|
use std::path::Path;
|
2016-06-09 17:34:25 +00:00
|
|
|
use std::path::PathBuf;
|
2018-10-23 13:39:23 +00:00
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use std::ops::Deref;
|
2016-06-09 17:34:25 +00:00
|
|
|
|
2018-01-08 22:34:13 +00:00
|
|
|
use libimagentryutil::isa::Is;
|
|
|
|
use libimagentryutil::isa::IsKindHeaderPathProvider;
|
2018-10-30 17:40:52 +00:00
|
|
|
use libimagerror::errors::ErrorMsg as EM;
|
2016-06-09 17:34:25 +00:00
|
|
|
|
2018-03-18 16:52:41 +00:00
|
|
|
use toml::Value;
|
2018-02-13 20:11:45 +00:00
|
|
|
use toml_query::read::TomlValueReadExt;
|
2018-02-18 17:00:41 +00:00
|
|
|
use toml_query::delete::TomlValueDeleteExt;
|
2018-03-18 16:52:41 +00:00
|
|
|
use toml_query::insert::TomlValueInsertExt;
|
2018-10-30 17:40:52 +00:00
|
|
|
use failure::Fallible as Result;
|
|
|
|
use failure::Error;
|
2018-10-23 13:39:23 +00:00
|
|
|
use failure::err_msg;
|
|
|
|
use failure::ResultExt;
|
2016-06-14 09:05:38 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
use hasher::Hasher;
|
2016-06-09 17:34:25 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
/// A configuration of "collection name" -> "collection path" mappings
|
|
|
|
///
|
|
|
|
/// Should be deserializeable from the configuration file right away, because we expect a
|
|
|
|
/// configuration like this in the config file:
|
|
|
|
///
|
|
|
|
/// ```toml
|
|
|
|
/// [ref.collections]
|
|
|
|
/// music = "/home/alice/music"
|
|
|
|
/// documents = "/home/alice/doc"
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// for example.
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
|
|
pub struct Config(BTreeMap<String, PathBuf>);
|
2016-06-09 17:34:25 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
impl Config {
|
|
|
|
pub fn new(map: BTreeMap<String, PathBuf>) -> Self {
|
|
|
|
Config(map)
|
|
|
|
}
|
|
|
|
}
|
2018-01-08 22:34:13 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
impl Deref for Config {
|
|
|
|
type Target = BTreeMap<String, PathBuf>;
|
2017-08-27 18:38:26 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
2018-03-18 16:52:41 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
provide_kindflag_path!(pub IsRef, "ref.is_ref");
|
2016-06-24 15:31:28 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
/// Fassade module
|
|
|
|
///
|
|
|
|
/// This module is necessary to build a generic fassade around the "entry with a (default) hasher to
|
|
|
|
/// represent the entry as a ref".
|
|
|
|
///
|
|
|
|
/// The module is for code-structuring only, all types in the module are exported publicly in the
|
|
|
|
/// supermodule.
|
|
|
|
pub mod fassade {
|
|
|
|
use std::marker::PhantomData;
|
2016-06-14 09:08:58 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
use libimagstore::store::Entry;
|
|
|
|
use libimagentryutil::isa::Is;
|
|
|
|
|
|
|
|
use failure::Fallible as Result;
|
|
|
|
use failure::Error;
|
2018-02-18 17:00:41 +00:00
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
use hasher::sha1::Sha1Hasher;
|
|
|
|
use hasher::Hasher;
|
|
|
|
use super::IsRef;
|
|
|
|
|
|
|
|
pub trait RefFassade {
|
|
|
|
fn is_ref(&self) -> Result<bool>;
|
|
|
|
fn as_ref_with_hasher<H: Hasher>(&self) -> RefWithHasher<H>;
|
|
|
|
fn as_ref_with_hasher_mut<H: Hasher>(&mut self) -> MutRefWithHasher<H>;
|
2016-06-14 09:09:19 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
impl RefFassade for Entry {
|
|
|
|
/// Check whether the underlying object is actually a ref
|
|
|
|
fn is_ref(&self) -> Result<bool> {
|
|
|
|
self.is::<IsRef>().map_err(Error::from)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_ref_with_hasher<H: Hasher>(&self) -> RefWithHasher<H> {
|
|
|
|
RefWithHasher::new(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_ref_with_hasher_mut<H: Hasher>(&mut self) -> MutRefWithHasher<H> {
|
|
|
|
MutRefWithHasher::new(self)
|
|
|
|
}
|
|
|
|
|
2016-06-09 17:34:25 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
pub struct RefWithHasher<'a, H: Hasher = Sha1Hasher>(pub(crate) &'a Entry, PhantomData<H>);
|
|
|
|
|
|
|
|
impl<'a, H> RefWithHasher<'a, H>
|
|
|
|
where H: Hasher
|
|
|
|
{
|
|
|
|
fn new(entry: &'a Entry) -> Self {
|
|
|
|
RefWithHasher(entry, PhantomData)
|
|
|
|
}
|
2016-06-09 17:34:25 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
pub struct MutRefWithHasher<'a, H: Hasher = Sha1Hasher>(pub(crate) &'a mut Entry, PhantomData<H>);
|
|
|
|
|
|
|
|
impl<'a, H> MutRefWithHasher<'a, H>
|
|
|
|
where H: Hasher
|
|
|
|
{
|
|
|
|
fn new(entry: &'a mut Entry) -> Self {
|
|
|
|
MutRefWithHasher(entry, PhantomData)
|
|
|
|
}
|
|
|
|
}
|
2018-02-13 20:11:45 +00:00
|
|
|
}
|
2018-10-23 13:39:23 +00:00
|
|
|
pub use self::fassade::*;
|
2016-06-24 15:39:58 +00:00
|
|
|
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
pub trait Ref {
|
|
|
|
|
|
|
|
/// Check whether the underlying object is actually a ref
|
|
|
|
fn is_ref(&self) -> Result<bool>;
|
|
|
|
|
2019-02-19 21:18:36 +00:00
|
|
|
fn get_path(&self, config: &Config) -> Result<PathBuf>;
|
|
|
|
|
|
|
|
fn get_relative_path(&self) -> Result<PathBuf>;
|
2018-10-23 13:39:23 +00:00
|
|
|
|
|
|
|
/// Get the stored hash.
|
|
|
|
fn get_hash(&self) -> Result<&str>;
|
|
|
|
|
|
|
|
/// Check whether the referenced file still matches its hash
|
|
|
|
fn hash_valid(&self, config: &Config) -> Result<bool>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, H: Hasher> Ref for RefWithHasher<'a, H> {
|
2016-06-24 15:39:58 +00:00
|
|
|
|
2018-02-13 20:11:45 +00:00
|
|
|
/// Check whether the underlying object is actually a ref
|
|
|
|
fn is_ref(&self) -> Result<bool> {
|
2018-10-23 13:39:23 +00:00
|
|
|
self.0.is::<IsRef>().map_err(Error::from)
|
2016-06-09 17:34:25 +00:00
|
|
|
}
|
|
|
|
|
2018-02-13 21:37:22 +00:00
|
|
|
fn get_hash(&self) -> Result<&str> {
|
2018-10-23 13:39:23 +00:00
|
|
|
let header_path = format!("ref.hash.{}", H::NAME);
|
|
|
|
self.0
|
|
|
|
.get_header()
|
|
|
|
.read(&header_path)
|
2018-10-30 17:40:52 +00:00
|
|
|
.map_err(Error::from)?
|
2018-10-23 13:39:23 +00:00
|
|
|
.ok_or_else(|| {
|
|
|
|
Error::from(EM::EntryHeaderFieldMissing("ref.hash.<hash>"))
|
|
|
|
})
|
2018-10-30 17:40:52 +00:00
|
|
|
.and_then(|v| {
|
2018-10-23 13:39:23 +00:00
|
|
|
v.as_str().ok_or_else(|| {
|
|
|
|
Error::from(EM::EntryHeaderTypeError2("ref.hash.<hash>", "string"))
|
|
|
|
})
|
2018-10-30 17:40:52 +00:00
|
|
|
})
|
2016-06-09 17:34:25 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 21:18:36 +00:00
|
|
|
/// Get the path of the actual file
|
|
|
|
fn get_path(&self, config: &Config) -> Result<PathBuf> {
|
|
|
|
use toml_query::read::TomlValueReadTypeExt;
|
|
|
|
|
|
|
|
let relpath = self.0
|
|
|
|
.get_header()
|
|
|
|
.read_string("ref.relpath")?
|
|
|
|
.map(PathBuf::from)
|
|
|
|
.ok_or_else(|| Error::from(EM::EntryHeaderFieldMissing("ref.relpath")))?;
|
|
|
|
|
|
|
|
let collection_name = self.0
|
|
|
|
.get_header()
|
|
|
|
.read_string("ref.collection")?
|
|
|
|
.ok_or_else(|| Error::from(EM::EntryHeaderFieldMissing("ref.collection")))?;
|
|
|
|
|
|
|
|
get_file_path(config, &collection_name, relpath)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the relative path, relative to the configured basepath
|
|
|
|
fn get_relative_path(&self) -> Result<PathBuf> {
|
2018-10-23 13:39:23 +00:00
|
|
|
self.0
|
|
|
|
.get_header()
|
2019-02-19 20:49:14 +00:00
|
|
|
.read("ref.relpath")
|
2018-10-30 17:40:52 +00:00
|
|
|
.map_err(Error::from)?
|
2019-02-19 20:49:14 +00:00
|
|
|
.ok_or_else(|| Error::from(EM::EntryHeaderFieldMissing("ref.relpath")))
|
2018-10-30 17:40:52 +00:00
|
|
|
.and_then(|v| {
|
|
|
|
v.as_str()
|
2019-02-19 20:49:14 +00:00
|
|
|
.ok_or_else(|| EM::EntryHeaderTypeError2("ref.relpath", "string"))
|
2018-10-30 17:40:52 +00:00
|
|
|
.map_err(Error::from)
|
|
|
|
})
|
2018-02-13 21:37:22 +00:00
|
|
|
.map(PathBuf::from)
|
2016-06-09 17:34:25 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
fn hash_valid(&self, config: &Config) -> Result<bool> {
|
|
|
|
let ref_header = self.0
|
|
|
|
.get_header()
|
|
|
|
.read("ref")?
|
|
|
|
.ok_or_else(|| err_msg("Header missing at 'ref'"))?;
|
|
|
|
|
|
|
|
let collection_name = ref_header
|
|
|
|
.read("collection")
|
|
|
|
.map_err(Error::from)?
|
|
|
|
.ok_or_else(|| err_msg("Header missing at 'ref.collection'"))?
|
|
|
|
.as_str()
|
|
|
|
.ok_or_else(|| Error::from(EM::EntryHeaderTypeError2("ref.hash.<hash>", "string")))?;
|
|
|
|
|
|
|
|
let path = ref_header
|
2019-02-19 20:49:14 +00:00
|
|
|
.read("relpath")
|
2018-10-23 13:39:23 +00:00
|
|
|
.map_err(Error::from)?
|
2019-02-19 20:49:14 +00:00
|
|
|
.ok_or_else(|| err_msg("Header missing at 'ref.relpath'"))?
|
2018-10-23 13:39:23 +00:00
|
|
|
.as_str()
|
2018-02-13 21:37:22 +00:00
|
|
|
.map(PathBuf::from)
|
2018-10-23 13:39:23 +00:00
|
|
|
.ok_or_else(|| Error::from(EM::EntryHeaderTypeError2("ref.hash.<hash>", "string")))?;
|
|
|
|
|
|
|
|
|
|
|
|
let file_path = get_file_path(config, collection_name.as_ref(), &path)?;
|
|
|
|
|
|
|
|
ref_header
|
|
|
|
.read(H::NAME)
|
|
|
|
.map_err(Error::from)?
|
|
|
|
.ok_or_else(|| format_err!("Header missing at 'ref.{}'", H::NAME))
|
|
|
|
.and_then(|v| {
|
|
|
|
v.as_str().ok_or_else(|| {
|
|
|
|
Error::from(EM::EntryHeaderTypeError2("ref.hash.<hash>", "string"))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.and_then(|hash| H::hash(file_path).map(|h| h == hash))
|
2016-07-14 18:37:32 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait MutRef {
|
|
|
|
fn remove_ref(&mut self) -> Result<()>;
|
|
|
|
|
|
|
|
/// Make a ref out of a normal (non-ref) entry.
|
|
|
|
///
|
|
|
|
/// If the entry is already a ref, this fails if `force` is false
|
|
|
|
fn make_ref<P, Coll>(&mut self, path: P, collection_name: Coll, config: &Config, force: bool)
|
|
|
|
-> Result<()>
|
|
|
|
where P: AsRef<Path>,
|
|
|
|
Coll: AsRef<str>;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a, H> MutRef for MutRefWithHasher<'a, H>
|
|
|
|
where H: Hasher
|
|
|
|
{
|
|
|
|
|
2018-02-18 17:00:41 +00:00
|
|
|
fn remove_ref(&mut self) -> Result<()> {
|
2018-10-23 13:39:23 +00:00
|
|
|
debug!("Removing 'ref' header section");
|
|
|
|
{
|
|
|
|
let header = self.0.get_header_mut();
|
|
|
|
trace!("header = {:?}", header);
|
|
|
|
|
|
|
|
let _ = header.delete("ref.relpath").context("Removing ref.relpath")?;
|
|
|
|
|
|
|
|
if let Some(hash_tbl) = header.read_mut("ref.hash")? {
|
|
|
|
match hash_tbl {
|
|
|
|
Value::Table(ref mut tbl) => *tbl = BTreeMap::new(),
|
|
|
|
_ => {
|
|
|
|
// should not happen
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let _ = header.delete("ref.hash").context("Removing ref.hash")?;
|
|
|
|
let _ = header.delete("ref.collection").context("Removing ref.collection")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!("Removing 'ref' header marker");
|
|
|
|
self.0.remove_isflag::<IsRef>().context("Removing ref")?;
|
|
|
|
|
|
|
|
let _ = self.0
|
|
|
|
.get_header_mut()
|
|
|
|
.delete("ref")
|
|
|
|
.context("Removing ref")?;
|
|
|
|
|
|
|
|
trace!("header = {:?}", self.0.get_header());
|
2018-02-18 17:00:41 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
/// Make a ref out of a normal (non-ref) entry.
|
|
|
|
///
|
|
|
|
/// `path` is the path to refer to,
|
|
|
|
///
|
|
|
|
/// # Warning
|
|
|
|
///
|
|
|
|
/// If the entry is already a ref, this fails if `force` is false
|
|
|
|
///
|
|
|
|
fn make_ref<P, Coll>(&mut self, path: P, collection_name: Coll, config: &Config, force: bool)
|
|
|
|
-> Result<()>
|
|
|
|
where P: AsRef<Path>,
|
|
|
|
Coll: AsRef<str>
|
|
|
|
{
|
|
|
|
trace!("Making ref out of {:?}", self.0);
|
|
|
|
trace!("Making ref with collection name {:?}", collection_name.as_ref());
|
|
|
|
trace!("Making ref with config {:?}", config);
|
|
|
|
trace!("Making ref forced = {}", force);
|
|
|
|
|
|
|
|
if self.0.get_header().read("ref.is_ref")?.is_some() && !force {
|
|
|
|
debug!("Entry is already a Ref!");
|
|
|
|
let _ = Err(err_msg("Entry is already a reference")).context("Making ref out of entry")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let file_path = get_file_path(config, collection_name.as_ref(), &path)?;
|
|
|
|
|
|
|
|
if !file_path.exists() {
|
|
|
|
let msg = format_err!("File '{:?}' does not exist", file_path);
|
|
|
|
let _ = Err(msg).context("Making ref out of entry")?;
|
|
|
|
}
|
|
|
|
|
2019-02-20 22:14:43 +00:00
|
|
|
debug!("Entry hashing = {}", file_path.display());
|
2018-10-23 13:39:23 +00:00
|
|
|
let _ = H::hash(&file_path)
|
2019-02-19 20:49:52 +00:00
|
|
|
.and_then(|hash| {
|
2019-02-20 22:14:43 +00:00
|
|
|
trace!("hash = {}", hash);
|
|
|
|
|
2019-02-19 20:49:52 +00:00
|
|
|
// stripping the prefix of "path"
|
2019-02-20 22:14:43 +00:00
|
|
|
let prefix = get_basepath(collection_name.as_ref(), config)?;
|
2019-02-19 20:49:52 +00:00
|
|
|
|
2019-02-20 22:14:43 +00:00
|
|
|
trace!("Stripping = {}", prefix.display());
|
|
|
|
let relpath = path.as_ref().strip_prefix(prefix)?;
|
|
|
|
|
|
|
|
trace!("Using relpath = {} to make header section", relpath.display());
|
2019-02-19 20:49:52 +00:00
|
|
|
make_header_section(hash, H::NAME, relpath, collection_name)
|
|
|
|
})
|
2018-10-23 13:39:23 +00:00
|
|
|
.and_then(|h| self.0.get_header_mut().insert("ref", h).map_err(Error::from))
|
|
|
|
.and_then(|_| self.0.set_isflag::<IsRef>())
|
|
|
|
.context("Making ref out of entry")?;
|
|
|
|
|
|
|
|
debug!("Setting is-ref flag");
|
|
|
|
self.0
|
|
|
|
.set_isflag::<IsRef>()
|
|
|
|
.context("Setting ref-flag")
|
|
|
|
.map_err(Error::from)
|
|
|
|
.map(|_| ())
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new header section for a "ref".
|
|
|
|
///
|
|
|
|
/// # Warning
|
|
|
|
///
|
|
|
|
/// The `relpath` _must_ be relative to the configured path for that collection.
|
|
|
|
pub(crate) fn make_header_section<P, C, H>(hash: String, hashname: H, relpath: P, collection: C)
|
|
|
|
-> Result<Value>
|
|
|
|
where P: AsRef<Path>,
|
|
|
|
C: AsRef<str>,
|
|
|
|
H: AsRef<str>,
|
|
|
|
{
|
|
|
|
let mut header_section = Value::Table(BTreeMap::new());
|
|
|
|
{
|
|
|
|
let relpath = relpath
|
|
|
|
.as_ref()
|
|
|
|
.to_str()
|
|
|
|
.map(String::from)
|
|
|
|
.ok_or_else(|| {
|
|
|
|
let msg = format_err!("UTF Error in '{:?}'", relpath.as_ref());
|
|
|
|
Error::from(msg)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let _ = header_section.insert("relpath", Value::String(relpath))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let mut hash_table = Value::Table(BTreeMap::new());
|
|
|
|
let _ = hash_table.insert(hashname.as_ref(), Value::String(hash))?;
|
|
|
|
let _ = header_section.insert("hash", hash_table)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let _ = header_section.insert("collection", Value::String(String::from(collection.as_ref())));
|
|
|
|
|
|
|
|
Ok(header_section)
|
|
|
|
}
|
|
|
|
|
2019-02-19 20:49:52 +00:00
|
|
|
fn get_basepath<'a, Coll: AsRef<str>>(collection_name: Coll, config: &'a Config) -> Result<&'a PathBuf> {
|
|
|
|
config.get(collection_name.as_ref())
|
|
|
|
.ok_or_else(|| format_err!("Collection {} seems not to exist in config",
|
|
|
|
collection_name.as_ref()))
|
|
|
|
.map_err(Error::from)
|
|
|
|
}
|
|
|
|
|
2018-10-23 13:39:23 +00:00
|
|
|
fn get_file_path<P>(config: &Config, collection_name: &str, path: P) -> Result<PathBuf>
|
|
|
|
where P: AsRef<Path>
|
|
|
|
{
|
|
|
|
config
|
|
|
|
.get(collection_name)
|
|
|
|
.map(PathBuf::clone)
|
|
|
|
.ok_or_else(|| {
|
|
|
|
format_err!("Configuration missing for collection: '{}'", collection_name)
|
|
|
|
})
|
|
|
|
.context("Making ref out of entry")
|
|
|
|
.map_err(Error::from)
|
|
|
|
.map(|p| {
|
|
|
|
let filepath = p.join(&path);
|
|
|
|
trace!("Found filepath: {:?}", filepath.display());
|
|
|
|
filepath
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
use libimagstore::store::Store;
|
|
|
|
use libimagstore::store::Entry;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
use hasher::Hasher;
|
|
|
|
|
|
|
|
fn setup_logging() {
|
|
|
|
let _ = ::env_logger::try_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_store() -> Store {
|
|
|
|
Store::new_inmemory(PathBuf::from("/"), &None).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TestHasher;
|
|
|
|
impl Hasher for TestHasher {
|
|
|
|
const NAME: &'static str = "Testhasher";
|
|
|
|
|
|
|
|
fn hash<P: AsRef<Path>>(path: P) -> Result<String> {
|
|
|
|
path.as_ref()
|
|
|
|
.to_str()
|
|
|
|
.map(String::from)
|
|
|
|
.ok_or_else(|| Error::from(err_msg("Failed to create test hash")))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_isref() {
|
|
|
|
setup_logging();
|
|
|
|
let store = get_store();
|
|
|
|
let entry = store.retrieve(PathBuf::from("test_isref")).unwrap();
|
|
|
|
|
|
|
|
assert!(!entry.is_ref().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_makeref() {
|
|
|
|
setup_logging();
|
|
|
|
let store = get_store();
|
|
|
|
let mut entry = store.retrieve(PathBuf::from("test_makeref")).unwrap();
|
2019-02-19 20:49:52 +00:00
|
|
|
let file = PathBuf::from("/tmp"); // has to exist
|
2018-10-23 13:39:23 +00:00
|
|
|
let collection_name = "some_collection";
|
|
|
|
let config = Config({
|
|
|
|
let mut c = BTreeMap::new();
|
2019-02-19 20:49:52 +00:00
|
|
|
c.insert(String::from("some_collection"), PathBuf::from("/"));
|
2018-10-23 13:39:23 +00:00
|
|
|
c
|
|
|
|
});
|
|
|
|
|
|
|
|
let r = entry.as_ref_with_hasher_mut::<TestHasher>().make_ref(file, collection_name, &config, false);
|
|
|
|
assert!(r.is_ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_makeref_isref() {
|
|
|
|
setup_logging();
|
|
|
|
let store = get_store();
|
|
|
|
let mut entry = store.retrieve(PathBuf::from("test_makeref_isref")).unwrap();
|
2019-02-19 20:49:52 +00:00
|
|
|
let file = PathBuf::from("/tmp"); // has to exists
|
2018-10-23 13:39:23 +00:00
|
|
|
let collection_name = "some_collection";
|
|
|
|
let config = Config({
|
|
|
|
let mut c = BTreeMap::new();
|
2019-02-19 20:49:52 +00:00
|
|
|
c.insert(String::from("some_collection"), PathBuf::from("/"));
|
2018-10-23 13:39:23 +00:00
|
|
|
c
|
|
|
|
});
|
|
|
|
|
|
|
|
let res = entry.as_ref_with_hasher_mut::<TestHasher>().make_ref(file, collection_name, &config, false);
|
|
|
|
assert!(res.is_ok(), "Expected to be ok: {:?}", res);
|
|
|
|
|
|
|
|
assert!(entry.as_ref_with_hasher::<TestHasher>().is_ref().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_makeref_is_ref_with_testhash() {
|
|
|
|
setup_logging();
|
|
|
|
let store = get_store();
|
|
|
|
let mut entry = store.retrieve(PathBuf::from("test_makeref_is_ref_with_testhash")).unwrap();
|
2019-02-19 20:49:52 +00:00
|
|
|
let file = PathBuf::from("/tmp"); // has to exist
|
2018-10-23 13:39:23 +00:00
|
|
|
let collection_name = "some_collection";
|
|
|
|
let config = Config({
|
|
|
|
let mut c = BTreeMap::new();
|
|
|
|
c.insert(String::from("some_collection"), PathBuf::from("/"));
|
|
|
|
c
|
|
|
|
});
|
|
|
|
|
|
|
|
assert!(entry.as_ref_with_hasher_mut::<TestHasher>().make_ref(file, collection_name, &config, false).is_ok());
|
|
|
|
|
|
|
|
let check_isstr = |entry: &Entry, location, shouldbe| {
|
|
|
|
let var = entry.get_header().read(location);
|
|
|
|
|
|
|
|
assert!(var.is_ok(), "{} is not Ok(_): {:?}", location, var);
|
|
|
|
let var = var.unwrap();
|
|
|
|
|
|
|
|
assert!(var.is_some(), "{} is not Some(_): {:?}", location, var);
|
|
|
|
let var = var.unwrap().as_str();
|
|
|
|
|
|
|
|
assert!(var.is_some(), "{} is not String: {:?}", location, var);
|
|
|
|
assert_eq!(var.unwrap(), shouldbe, "{} is not == {}", location, shouldbe);
|
|
|
|
};
|
|
|
|
|
2019-02-19 20:49:52 +00:00
|
|
|
check_isstr(&entry, "ref.relpath", "tmp");
|
|
|
|
check_isstr(&entry, "ref.hash.Testhasher", "/tmp"); // TestHasher hashes by returning the path itself
|
2018-10-23 13:39:23 +00:00
|
|
|
check_isstr(&entry, "ref.collection", "some_collection");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_makeref_remref() {
|
|
|
|
setup_logging();
|
|
|
|
let store = get_store();
|
|
|
|
let mut entry = store.retrieve(PathBuf::from("test_makeref_remref")).unwrap();
|
|
|
|
let file = PathBuf::from("/"); // has to exist
|
|
|
|
let collection_name = "some_collection";
|
|
|
|
let config = Config({
|
|
|
|
let mut c = BTreeMap::new();
|
|
|
|
c.insert(String::from("some_collection"), PathBuf::from("/"));
|
|
|
|
c
|
|
|
|
});
|
|
|
|
|
|
|
|
assert!(entry.as_ref_with_hasher_mut::<TestHasher>().make_ref(file, collection_name, &config, false).is_ok());
|
|
|
|
assert!(entry.as_ref_with_hasher::<TestHasher>().is_ref().unwrap());
|
|
|
|
let res = entry.as_ref_with_hasher_mut::<TestHasher>().remove_ref();
|
|
|
|
assert!(res.is_ok(), "Expected to be ok: {:?}", res);
|
|
|
|
assert!(!entry.as_ref_with_hasher::<TestHasher>().is_ref().unwrap());
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:37:32 +00:00
|
|
|
}
|
|
|
|
|