From cf19e0563c3aa2e07263640643da18d65cd49c3f Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 27 Aug 2017 20:38:26 +0200 Subject: [PATCH 1/5] Reorganize functionality in traits --- lib/entry/libimagentryref/Cargo.toml | 1 + lib/entry/libimagentryref/src/lib.rs | 3 + lib/entry/libimagentryref/src/lister.rs | 116 ++---- lib/entry/libimagentryref/src/reference.rs | 460 ++++++--------------- lib/entry/libimagentryref/src/refstore.rs | 286 +++++++++++++ lib/entry/libimagentryref/src/util.rs | 56 +++ 6 files changed, 510 insertions(+), 412 deletions(-) create mode 100644 lib/entry/libimagentryref/src/refstore.rs create mode 100644 lib/entry/libimagentryref/src/util.rs diff --git a/lib/entry/libimagentryref/Cargo.toml b/lib/entry/libimagentryref/Cargo.toml index 7b7763e8..6bc8f38d 100644 --- a/lib/entry/libimagentryref/Cargo.toml +++ b/lib/entry/libimagentryref/Cargo.toml @@ -19,6 +19,7 @@ log = "0.3" rust-crypto = "0.2" semver = "0.5" toml = "^0.4" +toml-query = "0.3.0" version = "2.0.1" walkdir = "1.0.*" diff --git a/lib/entry/libimagentryref/src/lib.rs b/lib/entry/libimagentryref/src/lib.rs index 86c187d0..dc4c10a1 100644 --- a/lib/entry/libimagentryref/src/lib.rs +++ b/lib/entry/libimagentryref/src/lib.rs @@ -36,6 +36,7 @@ extern crate crypto; extern crate itertools; extern crate semver; extern crate toml; +extern crate toml_query; extern crate version; extern crate walkdir; @@ -52,4 +53,6 @@ pub mod hasher; pub mod hashers; pub mod lister; pub mod reference; +pub mod refstore; pub mod result; +mod util; diff --git a/lib/entry/libimagentryref/src/lister.rs b/lib/entry/libimagentryref/src/lister.rs index 662ea9f6..af843327 100644 --- a/lib/entry/libimagentryref/src/lister.rs +++ b/lib/entry/libimagentryref/src/lister.rs @@ -20,13 +20,14 @@ use std::default::Default; use std::io::stdout; use std::io::Write; +use std::ops::Deref; use libimagentrylist::lister::Lister; use libimagentrylist::result::Result; use libimagerror::trace::trace_error; use libimagstore::store::FileLockEntry; -use libimagerror::into::IntoError; use libimagentrylist::error::ListErrorKind as LEK; +use libimagentrylist::error as lerror; use reference::Ref; use error::MapErrInto; @@ -88,15 +89,45 @@ impl Lister for RefLister { debug!("fold({:?}, {:?})", accu, entry); let r = accu.and_then(|_| { debug!("Listing Entry: {:?}", entry); - lister_fn(entry, - self.check_dead, - self.check_changed, - self.check_changed_content, - self.check_changed_permiss) + { + let is_dead = if self.check_dead { + if try!(entry.fs_link_exists()) { + "dead" + } else { + "alive" + } + } else { + "not checked" + }; + + let is_changed = if self.check_changed { + if check_changed(entry.deref()) { "changed" } else { "unchanged" } + } else { + "not checked" + }; + + let is_changed_content = if self.check_changed_content { + if check_changed_content(entry.deref()) { "changed" } else { "unchanged" } + } else { + "not checked" + }; + + let is_changed_permiss = if self.check_changed_permiss { + if check_changed_permiss(entry.deref()) { "changed" } else { "unchanged" } + } else { + "not checked" + }; + + Ok(format!("{} | {} | {} | {} | {} | {}", + is_dead, + is_changed, + is_changed_content, + is_changed_permiss, + entry.get_path_hash().unwrap_or_else(|_| String::from("Cannot get hash")), + entry.get_location())) + } .and_then(|s| { - write!(stdout(), "{}\n", s) - .map_err(Box::new) - .map_err(|e| LEK::FormatError.into_error_with_cause(e)) + lerror::MapErrInto::map_err_into(write!(stdout(), "{}\n", s), LEK::FormatError) }) .map_err_into(REK::RefToDisplayError) }) @@ -104,73 +135,16 @@ impl Lister for RefLister { (r, i + 1) }); debug!("Iterated over {} entries", n); - r.map_err(|e| LEK::FormatError.into_error_with_cause(Box::new(e))) + lerror::MapErrInto::map_err_into(r, LEK::FormatError) } } -fn lister_fn(fle: FileLockEntry, - do_check_dead: bool, - do_check_changed: bool, - do_check_changed_content: bool, - do_check_changed_permiss: bool) -> Result -{ - Ref::from_filelockentry(fle) - .map(|r| { - let is_dead = if do_check_dead { - if check_dead(&r) { "dead" } else { "alive" } - } else { - "not checked" - }; - - let is_changed = if do_check_changed { - if check_changed(&r) { "changed" } else { "unchanged" } - } else { - "not checked" - }; - - let is_changed_content = if do_check_changed_content { - if check_changed_content(&r) { "changed" } else { "unchanged" } - } else { - "not checked" - }; - - let is_changed_permiss = if do_check_changed_permiss { - if check_changed_permiss(&r) { "changed" } else { "unchanged" } - } else { - "not checked" - }; - - format!("{} | {} | {} | {} | {} | {}", - is_dead, - is_changed, - is_changed_content, - is_changed_permiss, - r.get_path_hash().unwrap_or_else(|_| String::from("Cannot get hash")), - r.get_location()) - }) - .map_err(|e| LEK::FormatError.into_error_with_cause(Box::new(e))) -} - -fn check_dead(r: &Ref) -> bool { - match r.fs_link_exists() { - Ok(b) => b, - Err(e) => { - warn!("Could not check whether the ref {} exists on the FS:", r); - trace_error(&e); - - // We continue here and tell the callee that this reference is dead, what is kind of - // true actually, as we might not have access to it right now - true - }, - } -} - -fn check_changed(r: &Ref) -> bool { +fn check_changed(r: &R) -> bool { check_changed_content(r) && check_changed_permiss(r) } -fn check_changed_content(r: &Ref) -> bool { +fn check_changed_content(r: &R) -> bool { let eq = r.get_current_hash() .and_then(|hash| r.get_stored_hash().map(|stored| (hash, stored))) .map(|(hash, stored)| hash == stored); @@ -178,7 +152,7 @@ fn check_changed_content(r: &Ref) -> bool { match eq { Ok(eq) => eq, Err(e) => { - warn!("Could not check whether the ref {} changed on the FS:", r); + warn!("Could not check whether the ref changed on the FS"); trace_error(&e); // We continue here and tell the callee that this reference is unchanged @@ -187,7 +161,7 @@ fn check_changed_content(r: &Ref) -> bool { } } -fn check_changed_permiss(_: &Ref) -> bool { +fn check_changed_permiss(_: &R) -> bool { warn!("Permission changes tracking not supported yet."); false } diff --git a/lib/entry/libimagentryref/src/reference.rs b/lib/entry/libimagentryref/src/reference.rs index e860452e..5263c765 100644 --- a/lib/entry/libimagentryref/src/reference.rs +++ b/lib/entry/libimagentryref/src/reference.rs @@ -21,18 +21,10 @@ //! files outside of the imag store. use std::path::PathBuf; -use std::ops::Deref; -use std::ops::DerefMut; -use std::collections::BTreeMap; use std::fs::File; -use std::fmt::{Display, Error as FmtError, Formatter}; use std::fs::Permissions; -use std::result::Result as RResult; -use libimagstore::store::FileLockEntry; -use libimagstore::storeid::StoreId; -use libimagstore::storeid::IntoStoreId; -use libimagstore::store::Store; +use libimagstore::store::Entry; use libimagstore::toml_ext::TomlValueExt; use libimagerror::into::IntoError; @@ -40,218 +32,92 @@ use toml::Value; use error::RefErrorKind as REK; use error::MapErrInto; -use flags::RefFlags; use result::Result; use hasher::*; -use module_path::ModuleEntryPath; -#[derive(Debug)] -pub struct Ref<'a>(FileLockEntry<'a>); - -impl<'a> Ref<'a> { - - /// Try to build a Ref object based on an existing FileLockEntry object - pub fn from_filelockentry(fle: FileLockEntry<'a>) -> Result> { - Ref::read_reference(&fle).map(|_| Ref(fle)) - } - - /// Try to get `si` as Ref object from the store - pub fn get(store: &'a Store, si: StoreId) -> Result> { - match store.get(si) { - Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))), - Ok(None) => return Err(REK::RefNotInStore.into_error()), - Ok(Some(fle)) => Ref::from_filelockentry(fle), - } - } - - /// Get a Ref object from the store by hash. - /// - /// Returns None if the hash cannot be found. - pub fn get_by_hash(store: &'a Store, hash: String) -> Result>> { - ModuleEntryPath::new(hash) - .into_storeid() - .and_then(|id| store.get(id)) - .map(|opt_fle| opt_fle.map(|fle| Ref(fle))) - .map_err(Box::new) - .map_err(|e| REK::StoreReadError.into_error_with_cause(e)) - } - - /// Delete a ref by hash - /// - /// If the returned Result contains an error, the ref might not be deleted. - pub fn delete_by_hash(store: &'a Store, hash: String) -> Result<()> { - ModuleEntryPath::new(hash) - .into_storeid() - .and_then(|id| store.delete(id)) - .map_err(Box::new) - .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) - } - - fn read_reference(fle: &FileLockEntry<'a>) -> Result { - match fle.get_header().read("ref.path") { - Ok(Some(Value::String(s))) => Ok(PathBuf::from(s)), - Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()), - Ok(None) => Err(REK::HeaderFieldMissingError.into_error()), - Err(e) => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))), - } - } - - pub fn create_with_hasher(store: &'a Store, pb: PathBuf, flags: RefFlags, mut h: H) - -> Result> - { - if !pb.exists() { - return Err(REK::RefTargetDoesNotExist.into_error()); - } - if flags.get_content_hashing() && pb.is_dir() { - return Err(REK::RefTargetCannotBeHashed.into_error()); - } - - let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold - try!(File::open(pb.clone()) - .map_err(Box::new) - .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e)) - - // If we were able to open this file, - // we hash the contents of the file and return (file, hash) - .and_then(|mut file| { - let opt_contenthash = if flags.get_content_hashing() { - Some(try!(h.create_hash(&pb, &mut file))) - } else { - None - }; - - Ok((file, opt_contenthash)) - }) - - // and then we get the permissions if we have to - // and return (file, content hash, permissions) - .and_then(|(file, opt_contenthash)| { - let opt_permissions = if flags.get_permission_tracking() { - Some(try!(file - .metadata() - .map(|md| md.permissions()) - .map_err(Box::new) - .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e)) - )) - } else { - None - }; - - Ok((opt_contenthash, opt_permissions)) - }) - - // and then we try to canonicalize the PathBuf, because we want to store a - // canonicalized path - // and return (file, content hash, permissions, canonicalized path) - .and_then(|(opt_contenthash, opt_permissions)| { - pb.canonicalize() - .map(|can| (opt_contenthash, opt_permissions, can)) - // if PathBuf::canonicalize() failed, build an error from the return value - .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e))) - }) - - // and then we hash the canonicalized path - // and return (file, content hash, permissions, canonicalized path, path hash) - .and_then(|(opt_contenthash, opt_permissions, can)| { - let path_hash = try!(Ref::hash_path(&can) - .map_err(Box::new) - .map_err(|e| REK::PathHashingError.into_error_with_cause(e)) - ); - - Ok((opt_contenthash, opt_permissions, can, path_hash)) - }) - - // and then we convert the PathBuf of the canonicalized path to a String to be able - // to save it in the Ref FileLockEntry obj - // and return - // (file, content hash, permissions, canonicalized path as String, path hash) - .and_then(|(opt_conhash, opt_perm, can, path_hash)| { - match can.to_str().map(String::from) { - // UTF convert error in PathBuf::to_str(), - None => Err(REK::PathUTF8Error.into_error()), - Some(can) => Ok((opt_conhash, opt_perm, can, path_hash)) - } - }) - - // and then we create the FileLockEntry in the Store - // and return (filelockentry, content hash, permissions, canonicalized path) - .and_then(|(opt_conhash, opt_perm, can, path_hash)| { - let fle = try!(store - .create(ModuleEntryPath::new(path_hash)) - .map_err(Box::new) - .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) - ); - - Ok((fle, opt_conhash, opt_perm, can)) - }) - ) - }; - - for tpl in [ - Some((String::from("ref"), Value::Table(BTreeMap::new()))), - Some((String::from("ref.permissions"), Value::Table(BTreeMap::new()))), - Some((String::from("ref.path"), Value::String(canonical_path))), - Some((String::from("ref.content_hash"), Value::Table(BTreeMap::new()))), - - content_hash.map(|hash| { - (format!("ref.content_hash.{}", h.hash_name()), Value::String(hash)) - }), - permissions.map(|p| { - (String::from("ref.permissions.ro"), Value::Boolean(p.readonly())) - }), - ].into_iter() - { - match tpl { - &Some((ref s, ref v)) => { - match fle.get_header_mut().insert(s, v.clone()) { - Ok(false) => { - let e = REK::HeaderFieldAlreadyExistsError.into_error(); - let e = Box::new(e); - let e = REK::HeaderFieldWriteError.into_error_with_cause(e); - return Err(e); - }, - Err(e) => { - let e = Box::new(e); - let e = REK::HeaderFieldWriteError.into_error_with_cause(e); - return Err(e); - }, - _ => (), - } - } - &None => { - debug!("Not going to insert."); - } - } - } - - Ok(Ref(fle)) - } - - /// Create a Ref object which refers to `pb` - pub fn create(store: &'a Store, pb: PathBuf, flags: RefFlags) -> Result> { - Ref::create_with_hasher(store, pb, flags, DefaultHasher::new()) - } - - /// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash - /// algorithm on it - fn hash_path(pb: &PathBuf) -> Result { - use crypto::sha1::Sha1; - use crypto::digest::Digest; - - match pb.to_str() { - Some(s) => { - let mut hasher = Sha1::new(); - hasher.input_str(s); - Ok(hasher.result_str()) - }, - None => return Err(REK::PathUTF8Error.into_error()), - } - } +pub trait Ref { /// Get the hash from the path of the ref - pub fn get_path_hash(&self) -> Result { - self.0 - .get_location() + fn get_path_hash(&self) -> Result; + + /// Get the hash of the link target which is stored in the ref object + fn get_stored_hash(&self) -> Result; + + /// Get the hahs of the link target which is stored in the ref object, which is hashed with a + /// custom Hasher instance. + fn get_stored_hash_with_hasher(&self, h: &H) -> Result; + + /// Get the hash of the link target by reading the link target and hashing the contents + fn get_current_hash(&self) -> Result; + + /// Get the hash of the link target by reading the link target and hashing the contents with the + /// custom hasher + fn get_current_hash_with_hasher(&self, h: H) -> Result; + + /// check whether the pointer the Ref represents still points to a file which exists + fn fs_link_exists(&self) -> Result; + + /// Alias for `r.fs_link_exists() && r.deref().is_file()` + fn is_ref_to_file(&self) -> Result; + + /// Alias for `r.fs_link_exists() && r.deref().is_dir()` + fn is_ref_to_dir(&self) -> Result; + + /// Alias for `!Ref::fs_link_exists()` + fn is_dangling(&self) -> Result; + + /// check whether the pointer the Ref represents is valid + /// This includes: + /// - Hashsum of the file is still the same as stored in the Ref + /// - file permissions are still valid + fn fs_link_valid(&self) -> Result; + + /// Check whether the file permissions of the referenced file are equal to the stored + /// permissions + fn fs_link_valid_permissions(&self) -> Result; + + /// Check whether the Hashsum of the referenced file is equal to the stored hashsum + fn fs_link_valid_hash(&self) -> Result; + + /// Update the Ref by re-checking the file from FS + /// This errors if the file is not present or cannot be read() + fn update_ref(&mut self) -> Result<()>; + + /// Update the Ref by re-checking the file from FS using the passed Hasher instance + /// This errors if the file is not present or cannot be read() + fn update_ref_with_hasher(&mut self, h: &H) -> Result<()>; + + /// Get the path of the file which is reffered to by this Ref + fn fs_file(&self) -> Result; + + /// Re-find a referenced file + /// + /// This function tries to re-find a ref by searching all directories in `search_roots` recursively + /// for a file which matches the hash of the Ref. + /// + /// If `search_roots` is `None`, it starts at the filesystem root `/`. + /// + /// If the target cannot be found, this yields a RefTargetDoesNotExist error kind. + /// + /// # Warning + /// + /// This option causes heavy I/O as it recursively searches the Filesystem. + fn refind(&self, search_roots: Option>) -> Result; + + /// See documentation of `Ref::refind()` + fn refind_with_hasher(&self, search_roots: Option>, h: H) + -> Result; + + /// Get the permissions of the file which are present + fn get_current_permissions(&self) -> Result; +} + + +impl Ref for Entry { + + /// Get the hash from the path of the ref + fn get_path_hash(&self) -> Result { + self.get_location() .clone() .into_pathbuf() .map_err_into(REK::StoreIdError) @@ -265,14 +131,14 @@ impl<'a> Ref<'a> { } /// Get the hash of the link target which is stored in the ref object - pub fn get_stored_hash(&self) -> Result { + fn get_stored_hash(&self) -> Result { self.get_stored_hash_with_hasher(&DefaultHasher::new()) } /// Get the hahs of the link target which is stored in the ref object, which is hashed with a /// custom Hasher instance. - pub fn get_stored_hash_with_hasher(&self, h: &H) -> Result { - match self.0.get_header().read(&format!("ref.content_hash.{}", h.hash_name())[..]) { + fn get_stored_hash_with_hasher(&self, h: &H) -> Result { + match self.get_header().read(&format!("ref.content_hash.{}", h.hash_name())[..]) { // content hash stored... Ok(Some(Value::String(s))) => Ok(s), @@ -288,13 +154,13 @@ impl<'a> Ref<'a> { } /// Get the hash of the link target by reading the link target and hashing the contents - pub fn get_current_hash(&self) -> Result { + fn get_current_hash(&self) -> Result { self.get_current_hash_with_hasher(DefaultHasher::new()) } /// Get the hash of the link target by reading the link target and hashing the contents with the /// custom hasher - pub fn get_current_hash_with_hasher(&self, mut h: H) -> Result { + fn get_current_hash_with_hasher(&self, mut h: H) -> Result { self.fs_file() .and_then(|pb| { File::open(pb.clone()) @@ -305,40 +171,23 @@ impl<'a> Ref<'a> { .and_then(|(path, mut file)| h.create_hash(&path, &mut file)) } - /// Get the permissions of the file which are present - fn get_current_permissions(&self) -> Result { - self.fs_file() - .and_then(|pb| { - File::open(pb) - .map_err(Box::new) - .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e)) - }) - .and_then(|file| { - file - .metadata() - .map(|md| md.permissions()) - .map_err(Box::new) - .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e)) - }) - } - /// check whether the pointer the Ref represents still points to a file which exists - pub fn fs_link_exists(&self) -> Result { + fn fs_link_exists(&self) -> Result { self.fs_file().map(|pathbuf| pathbuf.exists()) } /// Alias for `r.fs_link_exists() && r.deref().is_file()` - pub fn is_ref_to_file(&self) -> Result { + fn is_ref_to_file(&self) -> Result { self.fs_file().map(|pathbuf| pathbuf.is_file()) } /// Alias for `r.fs_link_exists() && r.deref().is_dir()` - pub fn is_ref_to_dir(&self) -> Result { + fn is_ref_to_dir(&self) -> Result { self.fs_file().map(|pathbuf| pathbuf.is_dir()) } /// Alias for `!Ref::fs_link_exists()` - pub fn is_dangling(&self) -> Result { + fn is_dangling(&self) -> Result { self.fs_link_exists().map(|b| !b) } @@ -346,7 +195,7 @@ impl<'a> Ref<'a> { /// This includes: /// - Hashsum of the file is still the same as stored in the Ref /// - file permissions are still valid - pub fn fs_link_valid(&self) -> Result { + fn fs_link_valid(&self) -> Result { match (self.fs_link_valid_permissions(), self.fs_link_valid_hash()) { (Ok(true) , Ok(true)) => Ok(true), (Ok(_) , Ok(_)) => Ok(false), @@ -357,8 +206,8 @@ impl<'a> Ref<'a> { /// Check whether the file permissions of the referenced file are equal to the stored /// permissions - pub fn fs_link_valid_permissions(&self) -> Result { - self.0 + fn fs_link_valid_permissions(&self) -> Result { + self .get_header() .read("ref.permissions.ro") .map_err(Box::new) @@ -376,7 +225,7 @@ impl<'a> Ref<'a> { } /// Check whether the Hashsum of the referenced file is equal to the stored hashsum - pub fn fs_link_valid_hash(&self) -> Result { + fn fs_link_valid_hash(&self) -> Result { let stored_hash = try!(self.get_stored_hash()); let current_hash = try!(self.get_current_hash()); Ok(stored_hash == current_hash) @@ -384,24 +233,24 @@ impl<'a> Ref<'a> { /// Update the Ref by re-checking the file from FS /// This errors if the file is not present or cannot be read() - pub fn update_ref(&mut self) -> Result<()> { + fn update_ref(&mut self) -> Result<()> { self.update_ref_with_hasher(&DefaultHasher::new()) } /// Update the Ref by re-checking the file from FS using the passed Hasher instance /// This errors if the file is not present or cannot be read() - pub fn update_ref_with_hasher(&mut self, h: &H) -> Result<()> { + fn update_ref_with_hasher(&mut self, h: &H) -> Result<()> { let current_hash = try!(self.get_current_hash()); // uses the default hasher let current_perm = try!(self.get_current_permissions()); - try!(self.0 + try!(self .get_header_mut() .set("ref.permissions.ro", Value::Boolean(current_perm.readonly())) .map_err(Box::new) .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) ); - try!(self.0 + try!(self .get_header_mut() .set(&format!("ref.content_hash.{}", h.hash_name())[..], Value::String(current_hash)) .map_err(Box::new) @@ -412,8 +261,8 @@ impl<'a> Ref<'a> { } /// Get the path of the file which is reffered to by this Ref - pub fn fs_file(&self) -> Result { - match self.0.get_header().read("ref.path") { + fn fs_file(&self) -> Result { + match self.get_header().read("ref.path") { Ok(Some(Value::String(ref s))) => Ok(PathBuf::from(s)), Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()), Ok(None) => Err(REK::HeaderFieldMissingError.into_error()), @@ -421,56 +270,6 @@ impl<'a> Ref<'a> { } } - /// Check whether there is a reference to the file at `pb` - pub fn exists(store: &Store, pb: PathBuf) -> Result { - pb.canonicalize() - .map_err(Box::new) - .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(e)) - .and_then(|can| { - Ref::hash_path(&can) - .map_err(Box::new) - .map_err(|e| REK::PathHashingError.into_error_with_cause(e)) - }) - .and_then(|hash| { - store.retrieve_for_module("ref").map(|iter| (hash, iter)) - .map_err(Box::new) - .map_err(|e| REK::StoreReadError.into_error_with_cause(e)) - }) - .and_then(|(hash, possible_refs)| { - // This is kind of a manual Iterator::filter() call what we do here, but with the - // actual ::filter method we cannot return the error in a nice way, so we do it - // manually here. If you can come up with a better version of this, feel free to - // take this note as a todo. - for r in possible_refs { - let contains_hash = try!(r.to_str() - .map_err_into(REK::TypeConversionError) - .map(|s| s.contains(&hash[..]))); - - if !contains_hash { - continue; - } - - match store.get(r) { - Ok(Some(fle)) => { - if Ref::read_reference(&fle).map(|path| path == pb).unwrap_or(false) { - return Ok(true) - } - }, - - Ok(None) => { // Something weird just happened - return Err(REK::StoreReadError.into_error()); - }, - - Err(e) => { - return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))); - }, - } - } - - Ok(false) - }) - } - /// Re-find a referenced file /// /// This function tries to re-find a ref by searching all directories in `search_roots` recursively @@ -483,11 +282,12 @@ impl<'a> Ref<'a> { /// # Warning /// /// This option causes heavy I/O as it recursively searches the Filesystem. - pub fn refind(&self, search_roots: Option>) -> Result { + fn refind(&self, search_roots: Option>) -> Result { self.refind_with_hasher(search_roots, DefaultHasher::new()) } - pub fn refind_with_hasher(&self, search_roots: Option>, mut h: H) + /// See documentation of `Ref::refind()` + fn refind_with_hasher(&self, search_roots: Option>, mut h: H) -> Result { use itertools::Itertools; @@ -534,43 +334,21 @@ impl<'a> Ref<'a> { }) } -} - -impl<'a> Deref for Ref<'a> { - type Target = FileLockEntry<'a>; - - fn deref(&self) -> &FileLockEntry<'a> { - &self.0 - } - -} - -impl<'a> DerefMut for Ref<'a> { - - fn deref_mut(&mut self) -> &mut FileLockEntry<'a> { - &mut self.0 - } - -} - -impl<'a> Display for Ref<'a> { - - fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> { - let path = self.fs_file() - .map(|pb| String::from(pb.to_str().unwrap_or(""))) - .unwrap_or(String::from("Could not read Path from reference object")); - - let hash = self.get_stored_hash().unwrap_or(String::from("")); - - write!(fmt, "Ref({} -> {})", hash, path) - } - -} - -impl<'a> Into> for Ref<'a> { - - fn into(self) -> FileLockEntry<'a> { - self.0 + /// Get the permissions of the file which are present + fn get_current_permissions(&self) -> Result { + self.fs_file() + .and_then(|pb| { + File::open(pb) + .map_err(Box::new) + .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e)) + }) + .and_then(|file| { + file + .metadata() + .map(|md| md.permissions()) + .map_err(Box::new) + .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e)) + }) } } diff --git a/lib/entry/libimagentryref/src/refstore.rs b/lib/entry/libimagentryref/src/refstore.rs new file mode 100644 index 00000000..5d4e1243 --- /dev/null +++ b/lib/entry/libimagentryref/src/refstore.rs @@ -0,0 +1,286 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// 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 +// + +use std::path::PathBuf; +use std::collections::BTreeMap; +use std::fs::File; + +use libimagstore::store::FileLockEntry; +use libimagstore::storeid::StoreId; +use libimagstore::storeid::IntoStoreId; +use libimagstore::store::Store; +use libimagstore::toml_ext::TomlValueExt; +use libimagerror::into::IntoError; + +use toml::Value; + +use error::RefErrorKind as REK; +use error::MapErrInto; +use flags::RefFlags; +use result::Result; +use hasher::*; +use module_path::ModuleEntryPath; +use util::*; + +pub trait RefStore { + + /// Check whether there is a reference to the file at `pb` + fn exists(&self, pb: PathBuf) -> Result; + + /// Try to get `si` as Ref object from the store + fn get<'a>(&'a self, si: StoreId) -> Result>; + + /// Get a Ref object from the store by hash. + /// + /// Returns None if the hash cannot be found. + fn get_by_hash<'a>(&'a self, hash: String) -> Result>>; + + /// Delete a ref by hash + /// + /// If the returned Result contains an error, the ref might not be deleted. + fn delete_by_hash(&self, hash: String) -> Result<()>; + + /// Create a Ref object which refers to `pb` + fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result>; + + fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, h: H) + -> Result>; + +} + +impl RefStore for Store { + + /// Check whether there is a reference to the file at `pb` + fn exists(&self, pb: PathBuf) -> Result { + pb.canonicalize() + .map_err(Box::new) + .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(e)) + .and_then(|can| { + hash_path(&can) + .map_err(Box::new) + .map_err(|e| REK::PathHashingError.into_error_with_cause(e)) + }) + .and_then(|hash| { + self.retrieve_for_module("ref").map(|iter| (hash, iter)) + .map_err(Box::new) + .map_err(|e| REK::StoreReadError.into_error_with_cause(e)) + }) + .and_then(|(hash, possible_refs)| { + // This is kind of a manual Iterator::filter() call what we do here, but with the + // actual ::filter method we cannot return the error in a nice way, so we do it + // manually here. If you can come up with a better version of this, feel free to + // take this note as a todo. + for r in possible_refs { + let contains_hash = try!(r.to_str() + .map_err_into(REK::TypeConversionError) + .map(|s| s.contains(&hash[..]))); + + if !contains_hash { + continue; + } + + match self.get(r) { + Ok(Some(fle)) => { + if read_reference(&fle).map(|path| path == pb).unwrap_or(false) { + return Ok(true) + } + }, + + Ok(None) => { // Something weird just happened + return Err(REK::StoreReadError.into_error()); + }, + + Err(e) => { + return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))); + }, + } + } + + Ok(false) + }) + } + + /// Try to get `si` as Ref object from the store + fn get<'a>(&'a self, si: StoreId) -> Result> { + match self.get(si) { + Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))), + Ok(None) => return Err(REK::RefNotInStore.into_error()), + Ok(Some(fle)) => Ok(fle), + } + } + + /// Get a Ref object from the store by hash. + /// + /// Returns None if the hash cannot be found. + fn get_by_hash<'a>(&'a self, hash: String) -> Result>> { + ModuleEntryPath::new(hash) + .into_storeid() + .and_then(|id| self.get(id)) + .map_err(Box::new) + .map_err(|e| REK::StoreReadError.into_error_with_cause(e)) + } + + /// Delete a ref by hash + /// + /// If the returned Result contains an error, the ref might not be deleted. + fn delete_by_hash(&self, hash: String) -> Result<()> { + ModuleEntryPath::new(hash) + .into_storeid() + .and_then(|id| self.delete(id)) + .map_err(Box::new) + .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) + } + + /// Create a Ref object which refers to `pb` + fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result> { + self.create_with_hasher(pb, flags, DefaultHasher::new()) + } + + fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, mut h: H) + -> Result> + { + if !pb.exists() { + return Err(REK::RefTargetDoesNotExist.into_error()); + } + if flags.get_content_hashing() && pb.is_dir() { + return Err(REK::RefTargetCannotBeHashed.into_error()); + } + + let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold + try!(File::open(pb.clone()) + .map_err(Box::new) + .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e)) + + // If we were able to open this file, + // we hash the contents of the file and return (file, hash) + .and_then(|mut file| { + let opt_contenthash = if flags.get_content_hashing() { + Some(try!(h.create_hash(&pb, &mut file))) + } else { + None + }; + + Ok((file, opt_contenthash)) + }) + + // and then we get the permissions if we have to + // and return (file, content hash, permissions) + .and_then(|(file, opt_contenthash)| { + let opt_permissions = if flags.get_permission_tracking() { + Some(try!(file + .metadata() + .map(|md| md.permissions()) + .map_err(Box::new) + .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e)) + )) + } else { + None + }; + + Ok((opt_contenthash, opt_permissions)) + }) + + // and then we try to canonicalize the PathBuf, because we want to store a + // canonicalized path + // and return (file, content hash, permissions, canonicalized path) + .and_then(|(opt_contenthash, opt_permissions)| { + pb.canonicalize() + .map(|can| (opt_contenthash, opt_permissions, can)) + // if PathBuf::canonicalize() failed, build an error from the return value + .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e))) + }) + + // and then we hash the canonicalized path + // and return (file, content hash, permissions, canonicalized path, path hash) + .and_then(|(opt_contenthash, opt_permissions, can)| { + let path_hash = try!(hash_path(&can) + .map_err(Box::new) + .map_err(|e| REK::PathHashingError.into_error_with_cause(e)) + ); + + Ok((opt_contenthash, opt_permissions, can, path_hash)) + }) + + // and then we convert the PathBuf of the canonicalized path to a String to be able + // to save it in the Ref FileLockEntry obj + // and return + // (file, content hash, permissions, canonicalized path as String, path hash) + .and_then(|(opt_conhash, opt_perm, can, path_hash)| { + match can.to_str().map(String::from) { + // UTF convert error in PathBuf::to_str(), + None => Err(REK::PathUTF8Error.into_error()), + Some(can) => Ok((opt_conhash, opt_perm, can, path_hash)) + } + }) + + // and then we create the FileLockEntry in the Store + // and return (filelockentry, content hash, permissions, canonicalized path) + .and_then(|(opt_conhash, opt_perm, can, path_hash)| { + let fle = try!(self + .create(ModuleEntryPath::new(path_hash)) + .map_err(Box::new) + .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) + ); + + Ok((fle, opt_conhash, opt_perm, can)) + }) + ) + }; + + for tpl in [ + Some((String::from("ref"), Value::Table(BTreeMap::new()))), + Some((String::from("ref.permissions"), Value::Table(BTreeMap::new()))), + Some((String::from("ref.path"), Value::String(canonical_path))), + Some((String::from("ref.content_hash"), Value::Table(BTreeMap::new()))), + + content_hash.map(|hash| { + (format!("ref.content_hash.{}", h.hash_name()), Value::String(hash)) + }), + permissions.map(|p| { + (String::from("ref.permissions.ro"), Value::Boolean(p.readonly())) + }), + ].into_iter() + { + match tpl { + &Some((ref s, ref v)) => { + match fle.get_header_mut().insert(s, v.clone()) { + Ok(false) => { + let e = REK::HeaderFieldAlreadyExistsError.into_error(); + let e = Box::new(e); + let e = REK::HeaderFieldWriteError.into_error_with_cause(e); + return Err(e); + }, + Err(e) => { + let e = Box::new(e); + let e = REK::HeaderFieldWriteError.into_error_with_cause(e); + return Err(e); + }, + _ => (), + } + } + &None => { + debug!("Not going to insert."); + } + } + } + + Ok(fle) + } + +} diff --git a/lib/entry/libimagentryref/src/util.rs b/lib/entry/libimagentryref/src/util.rs new file mode 100644 index 00000000..7f9a4730 --- /dev/null +++ b/lib/entry/libimagentryref/src/util.rs @@ -0,0 +1,56 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// 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 +// + +use std::path::PathBuf; + +use error::RefErrorKind as REK; +use result::Result; + +use libimagstore::store::Entry; +use libimagerror::into::IntoError; + +use toml::Value; +use toml_query::read::TomlValueReadExt; + +/// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash +/// algorithm on it +pub fn hash_path(pb: &PathBuf) -> Result { + use crypto::sha1::Sha1; + use crypto::digest::Digest; + + match pb.to_str() { + Some(s) => { + let mut hasher = Sha1::new(); + hasher.input_str(s); + Ok(hasher.result_str()) + }, + None => return Err(REK::PathUTF8Error.into_error()), + } +} + +/// Read the reference from a file +pub fn read_reference(refentry: &Entry) -> Result { + match refentry.get_header().read("ref.path") { + Ok(Some(&Value::String(ref s))) => Ok(PathBuf::from(s)), + Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()), + Ok(None) => Err(REK::HeaderFieldMissingError.into_error()), + Err(e) => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))), + } +} + From d58b97fdf1cf979720a01f8d23f3ce0c59c4eafc Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 27 Aug 2017 21:08:23 +0200 Subject: [PATCH 2/5] Light refactoring Use .map_err_into() instead of manual wrapping, some boilerplate minimized. --- .../libimagentryref/src/hashers/nbytes.rs | 11 +--- lib/entry/libimagentryref/src/reference.rs | 37 +++-------- lib/entry/libimagentryref/src/refstore.rs | 63 +++++++------------ 3 files changed, 34 insertions(+), 77 deletions(-) diff --git a/lib/entry/libimagentryref/src/hashers/nbytes.rs b/lib/entry/libimagentryref/src/hashers/nbytes.rs index 995e3dd8..5d190840 100644 --- a/lib/entry/libimagentryref/src/hashers/nbytes.rs +++ b/lib/entry/libimagentryref/src/hashers/nbytes.rs @@ -24,8 +24,6 @@ use std::result::Result as RResult; use crypto::sha1::Sha1; use crypto::digest::Digest; -use libimagerror::into::IntoError; - use hasher::Hasher; use result::Result; use error::RefErrorKind as REK; @@ -54,16 +52,13 @@ impl Hasher for NBytesHasher { } fn create_hash(&mut self, _: &PathBuf, contents: &mut R) -> Result { - let s = contents + let s = try!(contents .bytes() .take(self.n) .collect::, _>>() .map_err_into(REK::IOError) - .and_then(|v| String::from_utf8(v).map_err_into(REK::IOError)) - .map_err(Box::new) - .map_err(|e| REK::UTF8Error.into_error_with_cause(e)) - .map_err_into(REK::IOError); - self.hasher.input_str(&try!(s)[..]); + .and_then(|v| String::from_utf8(v).map_err_into(REK::UTF8Error))); + self.hasher.input_str(&s[..]); Ok(self.hasher.result_str()) } diff --git a/lib/entry/libimagentryref/src/reference.rs b/lib/entry/libimagentryref/src/reference.rs index 5263c765..0af07a2a 100644 --- a/lib/entry/libimagentryref/src/reference.rs +++ b/lib/entry/libimagentryref/src/reference.rs @@ -162,12 +162,7 @@ impl Ref for Entry { /// custom hasher fn get_current_hash_with_hasher(&self, mut h: H) -> Result { self.fs_file() - .and_then(|pb| { - File::open(pb.clone()) - .map(|f| (pb, f)) - .map_err(Box::new) - .map_err(|e| REK::IOError.into_error_with_cause(e)) - }) + .and_then(|pb| File::open(pb.clone()).map(|f| (pb, f)).map_err_into(REK::IOError)) .and_then(|(path, mut file)| h.create_hash(&path, &mut file)) } @@ -210,8 +205,7 @@ impl Ref for Entry { self .get_header() .read("ref.permissions.ro") - .map_err(Box::new) - .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e)) + .map_err_into(REK::HeaderFieldReadError) .and_then(|ro| { match ro { Some(Value::Boolean(b)) => Ok(b), @@ -220,8 +214,7 @@ impl Ref for Entry { } }) .and_then(|ro| self.get_current_permissions().map(|perm| ro == perm.readonly())) - .map_err(Box::new) - .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e)) + .map_err_into(REK::RefTargetCannotReadPermissions) } /// Check whether the Hashsum of the referenced file is equal to the stored hashsum @@ -246,15 +239,13 @@ impl Ref for Entry { try!(self .get_header_mut() .set("ref.permissions.ro", Value::Boolean(current_perm.readonly())) - .map_err(Box::new) - .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) + .map_err_into(REK::StoreWriteError) ); try!(self .get_header_mut() .set(&format!("ref.content_hash.{}", h.hash_name())[..], Value::String(current_hash)) - .map_err(Box::new) - .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) + .map_err_into(REK::StoreWriteError) ); Ok(()) @@ -304,13 +295,11 @@ impl Ref for Entry { .into_iter() .map(|entry| { entry - .map_err(Box::new) - .map_err(|e| REK::IOError.into_error_with_cause(e)) + .map_err_into(REK::IOError) .and_then(|entry| { let pb = PathBuf::from(entry.path()); File::open(entry.path()) - .map_err(Box::new) - .map_err(|e| REK::IOError.into_error_with_cause(e)) + .map_err_into(REK::IOError) .map(|f| (pb, f)) }) .and_then(|(p, mut f)| h.create_hash(&p, &mut f).map(|h| (p, h))) @@ -321,8 +310,7 @@ impl Ref for Entry { None } }) - .map_err(Box::new) - .map_err(|e| REK::IOError.into_error_with_cause(e)) + .map_err_into(REK::IOError) }) .filter_map(|e| e.ok()) .filter_map(|e| e) @@ -337,17 +325,12 @@ impl Ref for Entry { /// Get the permissions of the file which are present fn get_current_permissions(&self) -> Result { self.fs_file() - .and_then(|pb| { - File::open(pb) - .map_err(Box::new) - .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e)) - }) + .and_then(|pb| File::open(pb).map_err_into(REK::HeaderFieldReadError)) .and_then(|file| { file .metadata() .map(|md| md.permissions()) - .map_err(Box::new) - .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e)) + .map_err_into(REK::RefTargetCannotReadPermissions) }) } diff --git a/lib/entry/libimagentryref/src/refstore.rs b/lib/entry/libimagentryref/src/refstore.rs index 5d4e1243..e486d67a 100644 --- a/lib/entry/libimagentryref/src/refstore.rs +++ b/lib/entry/libimagentryref/src/refstore.rs @@ -69,17 +69,13 @@ impl RefStore for Store { /// Check whether there is a reference to the file at `pb` fn exists(&self, pb: PathBuf) -> Result { pb.canonicalize() - .map_err(Box::new) - .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(e)) - .and_then(|can| { - hash_path(&can) - .map_err(Box::new) - .map_err(|e| REK::PathHashingError.into_error_with_cause(e)) - }) + .map_err_into(REK::PathCanonicalizationError) + .and_then(|c| hash_path(&c)) + .map_err_into(REK::PathHashingError) .and_then(|hash| { - self.retrieve_for_module("ref").map(|iter| (hash, iter)) - .map_err(Box::new) - .map_err(|e| REK::StoreReadError.into_error_with_cause(e)) + self.retrieve_for_module("ref") + .map(|iter| (hash, iter)) + .map_err_into(REK::StoreReadError) }) .and_then(|(hash, possible_refs)| { // This is kind of a manual Iterator::filter() call what we do here, but with the @@ -89,7 +85,8 @@ impl RefStore for Store { for r in possible_refs { let contains_hash = try!(r.to_str() .map_err_into(REK::TypeConversionError) - .map(|s| s.contains(&hash[..]))); + .map(|s| s.contains(&hash[..])) + ); if !contains_hash { continue; @@ -102,13 +99,8 @@ impl RefStore for Store { } }, - Ok(None) => { // Something weird just happened - return Err(REK::StoreReadError.into_error()); - }, - - Err(e) => { - return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))); - }, + Ok(None) => return Err(REK::StoreReadError.into_error()), + Err(e) => return Err(e).map_err_into(REK::StoreReadError) } } @@ -119,7 +111,7 @@ impl RefStore for Store { /// Try to get `si` as Ref object from the store fn get<'a>(&'a self, si: StoreId) -> Result> { match self.get(si) { - Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))), + Err(e) => return Err(e).map_err_into(REK::StoreReadError), Ok(None) => return Err(REK::RefNotInStore.into_error()), Ok(Some(fle)) => Ok(fle), } @@ -132,8 +124,7 @@ impl RefStore for Store { ModuleEntryPath::new(hash) .into_storeid() .and_then(|id| self.get(id)) - .map_err(Box::new) - .map_err(|e| REK::StoreReadError.into_error_with_cause(e)) + .map_err_into(REK::StoreReadError) } /// Delete a ref by hash @@ -143,8 +134,7 @@ impl RefStore for Store { ModuleEntryPath::new(hash) .into_storeid() .and_then(|id| self.delete(id)) - .map_err(Box::new) - .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) + .map_err_into(REK::StoreWriteError) } /// Create a Ref object which refers to `pb` @@ -164,8 +154,7 @@ impl RefStore for Store { let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold try!(File::open(pb.clone()) - .map_err(Box::new) - .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e)) + .map_err_into(REK::RefTargetFileCannotBeOpened) // If we were able to open this file, // we hash the contents of the file and return (file, hash) @@ -186,8 +175,7 @@ impl RefStore for Store { Some(try!(file .metadata() .map(|md| md.permissions()) - .map_err(Box::new) - .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e)) + .map_err_into(REK::RefTargetCannotReadPermissions) )) } else { None @@ -203,16 +191,13 @@ impl RefStore for Store { pb.canonicalize() .map(|can| (opt_contenthash, opt_permissions, can)) // if PathBuf::canonicalize() failed, build an error from the return value - .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e))) + .map_err_into(REK::PathCanonicalizationError) }) // and then we hash the canonicalized path // and return (file, content hash, permissions, canonicalized path, path hash) .and_then(|(opt_contenthash, opt_permissions, can)| { - let path_hash = try!(hash_path(&can) - .map_err(Box::new) - .map_err(|e| REK::PathHashingError.into_error_with_cause(e)) - ); + let path_hash = try!(hash_path(&can).map_err_into(REK::PathHashingError)); Ok((opt_contenthash, opt_permissions, can, path_hash)) }) @@ -234,8 +219,7 @@ impl RefStore for Store { .and_then(|(opt_conhash, opt_perm, can, path_hash)| { let fle = try!(self .create(ModuleEntryPath::new(path_hash)) - .map_err(Box::new) - .map_err(|e| REK::StoreWriteError.into_error_with_cause(e)) + .map_err_into(REK::StoreWriteError) ); Ok((fle, opt_conhash, opt_perm, can)) @@ -262,15 +246,9 @@ impl RefStore for Store { match fle.get_header_mut().insert(s, v.clone()) { Ok(false) => { let e = REK::HeaderFieldAlreadyExistsError.into_error(); - let e = Box::new(e); - let e = REK::HeaderFieldWriteError.into_error_with_cause(e); - return Err(e); - }, - Err(e) => { - let e = Box::new(e); - let e = REK::HeaderFieldWriteError.into_error_with_cause(e); - return Err(e); + return Err(e).map_err_into(REK::HeaderFieldWriteError); }, + Err(e) => return Err(e).map_err_into(REK::HeaderFieldWriteError), _ => (), } } @@ -284,3 +262,4 @@ impl RefStore for Store { } } + From 6d40797a0755ada383261896605cab75ac2c3ecc Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Aug 2017 09:54:25 +0200 Subject: [PATCH 3/5] Fix libimagmail to use new libimagentryref interface --- lib/domain/libimagmail/src/iter.rs | 14 +++++++------- lib/domain/libimagmail/src/mail.rs | 16 +++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/domain/libimagmail/src/iter.rs b/lib/domain/libimagmail/src/iter.rs index fe1e72a4..6fee9e64 100644 --- a/lib/domain/libimagmail/src/iter.rs +++ b/lib/domain/libimagmail/src/iter.rs @@ -27,16 +27,17 @@ use mail::Mail; use result::Result; +use libimagstore::store::FileLockEntry; use libimagentryref::reference::Ref; use std::marker::PhantomData; -pub struct MailIter<'a, I: 'a + Iterator>> { - _marker: PhantomData<&'a I>, +pub struct MailIter<'a, I: Iterator>> { + _marker: PhantomData, i: I, } -impl<'a, I: Iterator>> MailIter<'a, I> { +impl<'a, I: Iterator>> MailIter<'a, I> { pub fn new(i: I) -> MailIter<'a, I> { MailIter { _marker: PhantomData, i: i } @@ -44,12 +45,11 @@ impl<'a, I: Iterator>> MailIter<'a, I> { } -impl<'a, I: Iterator>> Iterator for MailIter<'a, I> { - +impl<'a, I: Iterator>> Iterator for MailIter<'a, I> { type Item = Result>; - fn next(&mut self) -> Option>> { - self.i.next().map(Mail::from_ref) + fn next(&mut self) -> Option { + self.i.next().map(Mail::from_fle) } } diff --git a/lib/domain/libimagmail/src/mail.rs b/lib/domain/libimagmail/src/mail.rs index 11b57398..d5f02cdf 100644 --- a/lib/domain/libimagmail/src/mail.rs +++ b/lib/domain/libimagmail/src/mail.rs @@ -23,8 +23,10 @@ use std::fs::File; use std::io::Read; use libimagstore::store::Store; +use libimagstore::store::FileLockEntry; use libimagentryref::reference::Ref; use libimagentryref::flags::RefFlags; +use libimagentryref::refstore::RefStore; use email::MimeMessage; use email::results::ParsingResult as EmailParsingResult; @@ -47,7 +49,7 @@ impl From for Buffer { } } -pub struct Mail<'a>(Ref<'a>, Buffer); +pub struct Mail<'a>(FileLockEntry<'a>, Buffer); impl<'a> Mail<'a> { @@ -57,7 +59,7 @@ impl<'a> Mail<'a> { let f = RefFlags::default().with_content_hashing(true).with_permission_tracking(false); let p = PathBuf::from(p.as_ref()); - Ref::create_with_hasher(store, p, f, h) + store.create_with_hasher(p, f, h) .map_err_into(MEK::RefCreationError) .and_then(|reference| { reference.fs_file() @@ -76,19 +78,19 @@ impl<'a> Mail<'a> { /// Opens a mail by the passed hash pub fn open>(store: &Store, hash: S) -> Result> { - Ref::get_by_hash(store, String::from(hash.as_ref())) + store.get_by_hash(String::from(hash.as_ref())) .map_err_into(MEK::FetchByHashError) .map_err_into(MEK::FetchError) .and_then(|o| match o { - Some(r) => Mail::from_ref(r).map(Some), + Some(r) => Mail::from_fle(r).map(Some), None => Ok(None), }) } /// Implement me as TryFrom as soon as it is stable - pub fn from_ref(r: Ref<'a>) -> Result { - r.fs_file() + pub fn from_fle(fle: FileLockEntry<'a>) -> Result> { + fle.fs_file() .map_err_into(MEK::RefHandlingError) .and_then(|path| File::open(path).map_err_into(MEK::IOError)) .and_then(|mut file| { @@ -98,7 +100,7 @@ impl<'a> Mail<'a> { .map_err_into(MEK::IOError) }) .map(Buffer::from) - .map(|buffer| Mail(r, buffer)) + .map(|buffer| Mail(fle, buffer)) } pub fn get_field(&self, field: &str) -> Result> { From 7ff3985eaf6f2fddf1bd5651a4c77afdfd91a02b Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Aug 2017 10:43:51 +0200 Subject: [PATCH 4/5] Fix imag-mail to use new Ref interface --- bin/domain/imag-mail/src/main.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bin/domain/imag-mail/src/main.rs b/bin/domain/imag-mail/src/main.rs index 1eeb52bf..3091fbf2 100644 --- a/bin/domain/imag-mail/src/main.rs +++ b/bin/domain/imag-mail/src/main.rs @@ -33,6 +33,7 @@ extern crate libimagentryref; use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit}; use libimagmail::mail::Mail; use libimagentryref::reference::Ref; +use libimagentryref::refstore::RefStore; use libimagrt::runtime::Runtime; use libimagrt::setup::generate_runtime_setup; use libimagutil::debug_result::*; @@ -83,11 +84,11 @@ fn list(rt: &Runtime) { let iter = match store.retrieve_for_module("ref") { Ok(iter) => iter.filter_map(|id| { - Ref::get(store, id) - .map_err_into(MEK::RefHandlingError) - .and_then(|rf| Mail::from_ref(rf)) - .map_err_trace() - .ok() + match store.get(id).map_err_into(MEK::RefHandlingError).map_err_trace() { + Ok(Some(fle)) => Mail::from_fle(fle).map_err_trace().ok(), + Ok(None) => None, + Err(e) => trace_error_exit(&e, 1), + } }), Err(e) => trace_error_exit(&e, 1), }; From 0e6599f1922a972d5f8083b9aaf099c798d33915 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Aug 2017 14:55:33 +0200 Subject: [PATCH 5/5] Fix imag-ref --- bin/core/imag-ref/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/core/imag-ref/src/main.rs b/bin/core/imag-ref/src/main.rs index aa2f7d08..98d88f7b 100644 --- a/bin/core/imag-ref/src/main.rs +++ b/bin/core/imag-ref/src/main.rs @@ -50,7 +50,7 @@ use ui::build_ui; use std::path::PathBuf; -use libimagentryref::reference::Ref; +use libimagentryref::refstore::RefStore; use libimagentryref::flags::RefFlags; use libimagerror::trace::trace_error; use libimagrt::setup::generate_runtime_setup; @@ -84,7 +84,7 @@ fn add(rt: &Runtime) { .with_content_hashing(cmd.is_present("track-content")) .with_permission_tracking(cmd.is_present("track-permissions")); - match Ref::create(rt.store(), path, flags) { + match RefStore::create(rt.store(), path, flags) { Ok(r) => { debug!("Reference created: {:?}", r); info!("Ok"); @@ -104,7 +104,7 @@ fn remove(rt: &Runtime) { let yes = cmd.is_present("yes"); if yes || ask_bool(&format!("Delete Ref with hash '{}'", hash)[..], None) { - match Ref::delete_by_hash(rt.store(), hash) { + match rt.store().delete_by_hash(hash) { Err(e) => trace_error(&e), Ok(_) => info!("Ok"), } @@ -128,7 +128,7 @@ fn list(rt: &Runtime) { let iter = match rt.store().retrieve_for_module("ref") { Ok(iter) => iter.filter_map(|id| { - match Ref::get(rt.store(), id) { + match rt.store().get(id) { Ok(r) => Some(r), Err(e) => { trace_error(&e); @@ -147,7 +147,7 @@ fn list(rt: &Runtime) { .check_changed(do_check_changed) .check_changed_content(do_check_changed_content) .check_changed_permiss(do_check_changed_permiss) - .list(iter.map(|e| e.into())) + .list(iter.filter_map(Into::into)) .ok(); }