libimagstore: Move from error-chain to failure

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2018-10-30 18:40:50 +01:00
parent cc503920d0
commit 09e8619cf5
12 changed files with 269 additions and 506 deletions

View file

@ -29,8 +29,8 @@ walkdir = "2"
is-match = "0.1"
serde = "1"
serde_json = "1"
error-chain = "0.12"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
failure = "0.1"
libimagerror = { version = "0.9.0", path = "../../../lib/core/libimagerror" }
libimagutil = { version = "0.9.0", path = "../../../lib/etc/libimagutil" }

View file

@ -19,9 +19,11 @@
use toml::Value;
use store::Result;
use error::StoreError as SE;
use error::StoreErrorKind as SEK;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;
use libimagerror::errors::ErrorMsg as EM;
/// Checks whether the store configuration has a key "implicit-create" which maps to a boolean
/// value. If that key is present, the boolean is returned, otherwise false is returned.
@ -31,7 +33,10 @@ pub fn config_implicit_store_create_allowed(config: &Option<Value>) -> Result<bo
let key = "store.implicit-create";
if let Some(ref t) = *config {
t.read_bool(key)?.ok_or_else(|| SE::from_kind(SEK::ConfigKeyMissingError(key)))
t.read_bool(key)
.map_err(Error::from)
.context(EM::TomlQueryError)?
.ok_or_else(|| format_err!("Config key missing: {}", key))
} else {
Ok(false)
}

View file

@ -1,253 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> 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 storeid::StoreId;
error_chain! {
types {
StoreError, StoreErrorKind, ResultExt, Result;
}
foreign_links {
Io(::std::io::Error);
Fmt(::std::fmt::Error);
TomlDeserError(::toml::de::Error);
TomlSerError(::toml::ser::Error);
GlobPatternError(::glob::PatternError);
TomlQueryError(::toml_query::error::Error);
}
errors {
ConfigurationError {
description("Store Configuration Error")
display("Store Configuration Error")
}
ConfigTypeError(key: &'static str, expected: &'static str) {
description("Store configuration type error")
display("Store configuration type error at '{}', expected {}", key, expected)
}
ConfigKeyMissingError(key: &'static str) {
description("Configuration Key missing")
display("Configuration Key missing: '{}'", key)
}
VersionError {
description("Incompatible store versions detected")
display("Incompatible store versions detected")
}
CreateStoreDirDenied {
description("Creating store directory implicitely denied")
display("Creating store directory implicitely denied")
}
FileError {
description("File Error")
display("File Error")
}
IoError {
description("IO Error")
display("IO Error")
}
IdLocked {
description("ID locked")
display("ID locked")
}
IdNotFound(sid: StoreId) {
description("ID not found")
display("ID not found: {}", sid)
}
FileNotFound {
description("File corresponding to ID not found")
display("File corresponding to ID not found")
}
FileNotCreated {
description("File corresponding to ID could not be created")
display("File corresponding to ID could not be created")
}
FileNotWritten {
description("File corresponding to ID could not be written to")
display("File corresponding to ID could not be written to")
}
FileNotSeeked {
description("File corresponding to ID could not be seeked")
display("File corresponding to ID could not be seeked")
}
FileNotRemoved {
description("File corresponding to ID could not be removed")
display("File corresponding to ID could not be removed")
}
FileNotRenamed {
description("File corresponding to ID could not be renamed")
display("File corresponding to ID could not be renamed")
}
FileNotCopied {
description("File could not be copied")
display("File could not be copied")
}
DirNotCreated {
description("Directory/Directories could not be created")
display("Directory/Directories could not be created")
}
StorePathExists(pb: PathBuf) {
description("Store path exists")
display("Store path exists: {:?}", pb)
}
StorePathCreate(pb: PathBuf) {
description("Store path create")
display("Store path create: {:?}", pb)
}
LockError {
description("Error locking datastructure")
display("Error locking datastructure")
}
LockPoisoned {
description("The internal Store Lock has been poisoned")
display("The internal Store Lock has been poisoned")
}
EntryAlreadyBorrowed(id: StoreId) {
description("Entry is already borrowed")
display("Entry is already borrowed: {:?}", id)
}
EntryAlreadyExists(id: StoreId) {
description("Entry already exists")
display("Entry already exists: {:?}", id)
}
MalformedEntry {
description("Entry has invalid formatting, missing header")
display("Entry has invalid formatting, missing header")
}
HeaderTypeFailure {
description("Header type is wrong")
display("Header type is wrong")
}
EncodingError {
description("Encoding error")
display("Encoding error")
}
EntryRenameError(old: PathBuf, new: PathBuf) {
description("Entry rename error")
display("Entry rename error: {:?} -> {:?}", old, new)
}
StoreIdHandlingError {
description("StoreId handling error")
display("StoreId handling error")
}
StoreIdLocalPartAbsoluteError(pb: PathBuf) {
description("StoreId 'id' part is absolute (starts with '/') which is not allowed")
display("StoreId 'id' part is absolute (starts with '/') which is not allowed: {:?}", pb)
}
StoreIdBuildFromFullPathError {
description("Building StoreId from full file path failed")
display("Building StoreId from full file path failed")
}
StoreIdHasNoBaseError(pb: PathBuf) {
description("StoreId has no 'base' part")
display("StoreId has no 'base' part: {:?}", pb)
}
CreateCallError(sid: StoreId) {
description("Error when calling create()")
display("Error when calling create({:?})", sid)
}
RetrieveCallError(sid: StoreId) {
description("Error when calling retrieve()")
display("Error when calling retrieve({:?})", sid)
}
GetCallError(sid: StoreId) {
description("Error when calling get()")
display("Error when calling get({:?})", sid)
}
UpdateCallError(sid: StoreId) {
description("Error when calling update()")
display("Error when calling update({:?})", sid)
}
RetrieveCopyCallError(sid: StoreId) {
description("Error when calling retrieve_copy()")
display("Error when calling retrieve_copy({:?})", sid)
}
DeleteCallError(sid: StoreId) {
description("Error when calling delete()")
display("Error when calling delete({:?})", sid)
}
MoveCallError(old: StoreId, new: StoreId) {
description("Error when calling move()")
display("Error when calling move({:?} -> {:?})", old, new)
}
// Parser-related errors
MissingMainSection {
description("Missing main section")
display("Missing main section")
}
MissingVersionInfo {
description("Missing version information in main section")
display("Missing version information in main section")
}
NonTableInBaseTable {
description("A non-table was found in the base table")
display("A non-table was found in the base table")
}
HeaderInconsistency {
description("The header is inconsistent")
display("The header is inconsistent")
}
}
}

View file

@ -22,8 +22,7 @@ use std::io::{Seek, SeekFrom, Read};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use error::{StoreError as SE, StoreErrorKind as SEK};
use error::ResultExt;
use libimagerror::errors::ErrorMsg as EM;
use super::FileAbstraction;
use super::FileAbstractionInstance;
@ -34,6 +33,9 @@ use file_abstraction::iter::PathIterator;
use file_abstraction::iter::PathIterBuilder;
use walkdir::WalkDir;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
#[derive(Debug)]
pub struct FSFileAbstractionInstance(PathBuf);
@ -43,34 +45,41 @@ impl FileAbstractionInstance for FSFileAbstractionInstance {
/**
* Get the content behind this file
*/
fn get_file_content(&mut self, id: StoreId) -> Result<Entry, SE> {
fn get_file_content(&mut self, id: StoreId) -> Result<Option<Entry>> {
debug!("Getting lazy file: {:?}", self);
let mut file = open_file(&self.0)
.chain_err(|| SEK::FileNotFound)?;
let mut file = match open_file(&self.0) {
Err(err) => return Err(Error::from(err)),
Ok(None) => return Ok(None),
Ok(Some(file)) => file,
};
file.seek(SeekFrom::Start(0)).chain_err(|| SEK::FileNotSeeked)?;
file.seek(SeekFrom::Start(0)).context(EM::FileNotSeeked)?;
let mut s = String::new();
file.read_to_string(&mut s)
.chain_err(|| SEK::IoError)
.context(EM::IO)
.map_err(Error::from)
.map(|_| s)
.and_then(|s| Entry::from_str(id, &s))
.and_then(|s: String| Entry::from_str(id, &s))
.map(Some)
}
/**
* Write the content of this file
*/
fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE> {
fn write_file_content(&mut self, buf: &Entry) -> Result<()> {
use std::io::Write;
let buf = buf.to_str()?.into_bytes();
let mut file = create_file(&self.0).chain_err(|| SEK::FileNotCreated)?;
let mut file = create_file(&self.0).context(EM::FileNotCreated)?;
file.seek(SeekFrom::Start(0)).chain_err(|| SEK::FileNotCreated)?;
file.set_len(buf.len() as u64).chain_err(|| SEK::FileNotWritten)?;
file.write_all(&buf).chain_err(|| SEK::FileNotWritten)
file.seek(SeekFrom::Start(0)).context(EM::FileNotCreated)?;
file.set_len(buf.len() as u64).context(EM::FileNotWritten)?;
file.write_all(&buf)
.context(EM::FileNotWritten)
.map_err(Error::from)
}
}
@ -82,19 +91,24 @@ pub struct FSFileAbstraction {}
impl FileAbstraction for FSFileAbstraction {
fn remove_file(&self, path: &PathBuf) -> Result<(), SE> {
remove_file(path).chain_err(|| SEK::FileNotRemoved)
fn remove_file(&self, path: &PathBuf) -> Result<()> {
remove_file(path)
.context(EM::FileNotRemoved)
.map_err(Error::from)
}
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
copy(from, to).chain_err(|| SEK::FileNotCopied).map(|_| ())
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
copy(from, to)
.map(|_| ())
.context(EM::FileNotCopied)
.map_err(Error::from)
}
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
match to.parent() {
Some(p) => if !p.exists() {
debug!("Creating: {:?}", p);
let _ = create_dir_all(&PathBuf::from(p))?;
let _ = create_dir_all(&PathBuf::from(p)).context(EM::DirNotCreated)?;
},
None => {
debug!("Failed to find parent. This looks like it will fail now");
@ -103,19 +117,23 @@ impl FileAbstraction for FSFileAbstraction {
}
debug!("Renaming {:?} to {:?}", from, to);
rename(from, to).chain_err(|| SEK::FileNotRenamed)
rename(from, to)
.context(EM::FileNotRenamed)
.map_err(Error::from)
}
fn create_dir_all(&self, path: &PathBuf) -> Result<(), SE> {
fn create_dir_all(&self, path: &PathBuf) -> Result<()> {
debug!("Creating: {:?}", path);
create_dir_all(path).chain_err(|| SEK::DirNotCreated)
create_dir_all(path)
.context(EM::DirNotCreated)
.map_err(Error::from)
}
fn exists(&self, path: &PathBuf) -> Result<bool, SE> {
fn exists(&self, path: &PathBuf) -> Result<bool> {
Ok(path.exists())
}
fn is_file(&self, path: &PathBuf) -> Result<bool, SE> {
fn is_file(&self, path: &PathBuf) -> Result<bool> {
Ok(path.is_file())
}
@ -124,13 +142,13 @@ impl FileAbstraction for FSFileAbstraction {
}
/// We return nothing from the FS here.
fn drain(&self) -> Result<Drain, SE> {
fn drain(&self) -> Result<Drain> {
Ok(Drain::empty())
}
/// FileAbstraction::fill implementation that consumes the Drain and writes everything to the
/// filesystem
fn fill(&mut self, mut d: Drain) -> Result<(), SE> {
fn fill(&mut self, mut d: Drain) -> Result<()> {
d.iter()
.fold(Ok(()), |acc, (path, element)| {
acc.and_then(|_| self.new_instance(path).write_file_content(&element))
@ -141,7 +159,7 @@ impl FileAbstraction for FSFileAbstraction {
basepath: PathBuf,
storepath: PathBuf,
backend: Arc<FileAbstraction>)
-> Result<PathIterator, SE>
-> Result<PathIterator>
{
trace!("Building PathIterator object");
Ok(PathIterator::new(Box::new(WalkDirPathIterBuilder { basepath }), storepath, backend))
@ -153,13 +171,15 @@ pub(crate) struct WalkDirPathIterBuilder {
}
impl PathIterBuilder for WalkDirPathIterBuilder {
fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf, SE>>> {
fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf>>> {
Box::new(WalkDir::new(self.basepath.clone())
.min_depth(1)
.max_open(100)
.into_iter()
.map(|r| {
r.map(|e| PathBuf::from(e.path())).chain_err(|| SE::from_kind(SEK::FileError))
r.map(|e| PathBuf::from(e.path()))
.context(format_err!("Error in Walkdir"))
.map_err(Error::from)
}))
}
@ -168,8 +188,16 @@ impl PathIterBuilder for WalkDirPathIterBuilder {
}
}
fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
OpenOptions::new().write(true).read(true).open(p)
fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<Option<File>> {
match OpenOptions::new().write(true).read(true).open(p) {
Err(e) => {
match e.kind() {
::std::io::ErrorKind::NotFound => return Ok(None),
_ => return Err(e),
}
},
Ok(file) => Ok(Some(file))
}
}
fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {

View file

@ -18,15 +18,17 @@
//
use std::path::PathBuf;
use error::StoreError as SE;
use error::StoreErrorKind as SEK;
use std::collections::HashMap;
use std::sync::Mutex;
use std::cell::RefCell;
use std::sync::Arc;
use std::ops::Deref;
use libimagerror::errors::ErrorMsg as EM;
use failure::Fallible as Result;
use failure::Error;
use super::FileAbstraction;
use super::FileAbstractionInstance;
use super::Drain;
@ -62,21 +64,21 @@ impl FileAbstractionInstance for InMemoryFileAbstractionInstance {
/**
* Get the mutable file behind a InMemoryFileAbstraction object
*/
fn get_file_content(&mut self, _: StoreId) -> Result<Entry, SE> {
fn get_file_content(&mut self, _: StoreId) -> Result<Option<Entry>> {
debug!("Getting lazy file: {:?}", self);
self.fs_abstraction
.lock()
.map_err(|_| SE::from_kind(SEK::LockError))
.and_then(|mut mtx| {
.map_err(|_| Error::from(EM::LockError))
.map(|mut mtx| {
mtx.get_mut()
.get(&self.absent_path)
.cloned()
.ok_or_else(|| SE::from_kind(SEK::FileNotFound))
})
.map_err(Error::from)
}
fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE> {
fn write_file_content(&mut self, buf: &Entry) -> Result<()> {
match *self {
InMemoryFileAbstractionInstance { ref absent_path, .. } => {
let mut mtx = self.fs_abstraction.lock().expect("Locking Mutex failed");
@ -99,18 +101,19 @@ impl InMemoryFileAbstraction {
&self.virtual_filesystem
}
fn backend_cloned<'a>(&'a self) -> Result<HashMap<PathBuf, Entry>, SE> {
fn backend_cloned<'a>(&'a self) -> Result<HashMap<PathBuf, Entry>> {
self.virtual_filesystem
.lock()
.map_err(|_| SE::from_kind(SEK::LockError))
.map_err(|_| Error::from(EM::LockError))
.map(|mtx| mtx.deref().borrow().clone())
.into()
}
}
impl FileAbstraction for InMemoryFileAbstraction {
fn remove_file(&self, path: &PathBuf) -> Result<(), SE> {
fn remove_file(&self, path: &PathBuf) -> Result<()> {
debug!("Removing: {:?}", path);
self.backend()
.lock()
@ -118,43 +121,43 @@ impl FileAbstraction for InMemoryFileAbstraction {
.get_mut()
.remove(path)
.map(|_| ())
.ok_or_else(|| SE::from_kind(SEK::FileNotFound))
.ok_or_else(|| EM::FileNotFound.into())
}
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
debug!("Copying : {:?} -> {:?}", from, to);
let mut mtx = self.backend().lock().expect("Locking Mutex failed");
let backend = mtx.get_mut();
let a = backend.get(from).cloned().ok_or_else(|| SE::from_kind(SEK::FileNotFound))?;
let a = backend.get(from).cloned().ok_or_else(|| EM::FileNotFound)?;
backend.insert(to.clone(), a);
debug!("Copying: {:?} -> {:?} worked", from, to);
Ok(())
}
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
debug!("Renaming: {:?} -> {:?}", from, to);
let mut mtx = self.backend().lock().expect("Locking Mutex failed");
let backend = mtx.get_mut();
let a = backend.remove(from).ok_or_else(|| SE::from_kind(SEK::FileNotFound))?;
let a = backend.get(from).cloned().ok_or_else(|| EM::FileNotFound)?;
backend.insert(to.clone(), a);
debug!("Renaming: {:?} -> {:?} worked", from, to);
Ok(())
}
fn create_dir_all(&self, _: &PathBuf) -> Result<(), SE> {
fn create_dir_all(&self, _: &PathBuf) -> Result<()> {
Ok(())
}
fn exists(&self, pb: &PathBuf) -> Result<bool, SE> {
fn exists(&self, pb: &PathBuf) -> Result<bool> {
let mut mtx = self.backend().lock().expect("Locking Mutex failed");
let backend = mtx.get_mut();
Ok(backend.contains_key(pb))
}
fn is_file(&self, pb: &PathBuf) -> Result<bool, SE> {
fn is_file(&self, pb: &PathBuf) -> Result<bool> {
// Because we only store Entries in the memory-internal backend, we only have to check for
// existance here, as if a path exists in the inmemory storage, it is always mapped to an
// entry. hence it is always a path to a file
@ -165,13 +168,15 @@ impl FileAbstraction for InMemoryFileAbstraction {
Box::new(InMemoryFileAbstractionInstance::new(self.backend().clone(), p))
}
fn drain(&self) -> Result<Drain, SE> {
fn drain(&self) -> Result<Drain> {
self.backend_cloned().map(Drain::new)
}
fn fill<'a>(&'a mut self, mut d: Drain) -> Result<(), SE> {
fn fill<'a>(&'a mut self, mut d: Drain) -> Result<()> {
debug!("Draining into : {:?}", self);
let mut mtx = self.backend().lock().map_err(|_| SEK::LockError)?;
let mut mtx = self.backend()
.lock()
.map_err(|_| EM::LockError)?;
let backend = mtx.get_mut();
for (path, element) in d.iter() {
@ -182,17 +187,17 @@ impl FileAbstraction for InMemoryFileAbstraction {
Ok(())
}
fn pathes_recursively(&self, _basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator, SE> {
fn pathes_recursively(&self, _basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator> {
trace!("Building PathIterator object (inmemory implementation)");
let keys : Vec<PathBuf> = self
.backend()
.lock()
.map_err(|_| SE::from_kind(SEK::FileError))?
.map_err(|_| EM::LockError)?
.get_mut()
.keys()
.map(PathBuf::from)
.map(Ok)
.collect::<Result<_, SE>>()?; // we have to collect() because of the lock() above.
.collect::<Result<_>>()?; // we have to collect() because of the lock() above.
Ok(PathIterator::new(Box::new(InMemPathIterBuilder(keys)), storepath, backend))
}
@ -201,7 +206,7 @@ impl FileAbstraction for InMemoryFileAbstraction {
pub(crate) struct InMemPathIterBuilder(Vec<PathBuf>);
impl PathIterBuilder for InMemPathIterBuilder {
fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf, SE>>> {
fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf>>> {
Box::new(self.0.clone().into_iter().map(Ok))
}

View file

@ -20,7 +20,8 @@
use std::path::PathBuf;
use std::sync::Arc;
use error::Result;
use failure::Fallible as Result;
use storeid::StoreId;
use file_abstraction::FileAbstraction;

View file

@ -22,7 +22,8 @@ use std::fmt::Debug;
use std::collections::HashMap;
use std::sync::Arc;
use error::StoreError as SE;
use failure::Fallible as Result;
use store::Entry;
use storeid::StoreId;
@ -38,20 +39,20 @@ use self::iter::PathIterator;
/// An abstraction trait over filesystem actions
pub trait FileAbstraction : Debug {
fn remove_file(&self, path: &PathBuf) -> Result<(), SE>;
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE>;
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE>;
fn create_dir_all(&self, _: &PathBuf) -> Result<(), SE>;
fn remove_file(&self, path: &PathBuf) -> Result<()>;
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()>;
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()>;
fn create_dir_all(&self, _: &PathBuf) -> Result<()>;
fn exists(&self, &PathBuf) -> Result<bool, SE>;
fn is_file(&self, &PathBuf) -> Result<bool, SE>;
fn exists(&self, &PathBuf) -> Result<bool>;
fn is_file(&self, &PathBuf) -> Result<bool>;
fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance>;
fn drain(&self) -> Result<Drain, SE>;
fn fill<'a>(&'a mut self, d: Drain) -> Result<(), SE>;
fn drain(&self) -> Result<Drain>;
fn fill<'a>(&'a mut self, d: Drain) -> Result<()>;
fn pathes_recursively(&self, basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator, SE>;
fn pathes_recursively(&self, basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator>;
}
/// An abstraction trait over actions on files
@ -61,8 +62,8 @@ pub trait FileAbstractionInstance : Debug {
///
/// The `StoreId` is passed because the backend does not know where the Entry lives, but the
/// Entry type itself must be constructed with the id.
fn get_file_content(&mut self, id: StoreId) -> Result<Entry, SE>;
fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE>;
fn get_file_content(&mut self, id: StoreId) -> Result<Option<Entry>>;
fn write_file_content(&mut self, buf: &Entry) -> Result<()>;
}
pub struct Drain(HashMap<PathBuf, Entry>);
@ -119,7 +120,7 @@ version = "{}"
Hello World"#, env!("CARGO_PKG_VERSION"))).unwrap();
lf.write_file_content(&file).unwrap();
let bah = lf.get_file_content(loca).unwrap();
let bah = lf.get_file_content(loca).unwrap().unwrap();
assert_eq!(bah.get_content(), "Hello World");
}
@ -140,7 +141,7 @@ Hello World
baz"#, env!("CARGO_PKG_VERSION"))).unwrap();
lf.write_file_content(&file).unwrap();
let bah = lf.get_file_content(loca).unwrap();
let bah = lf.get_file_content(loca).unwrap().unwrap();
assert_eq!(bah.get_content(), "Hello World\nbaz");
}
@ -163,7 +164,7 @@ baz
"#, env!("CARGO_PKG_VERSION"))).unwrap();
lf.write_file_content(&file).unwrap();
let bah = lf.get_file_content(loca).unwrap();
let bah = lf.get_file_content(loca).unwrap().unwrap();
assert_eq!(bah.get_content(), "Hello World\nbaz\n\n");
}

View file

@ -31,41 +31,34 @@ macro_rules! mk_iterator_mod {
#[allow(unused_imports)]
use store::FileLockEntry;
use store::Store;
use error::StoreError;
use std::result::Result as RResult;
use failure::Fallible as Result;
pub struct $itername<'a, E>(Box<Iterator<Item = RResult<StoreId, E>> + 'a>, &'a Store)
where E: From<StoreError>;
pub struct $itername<'a>(Box<Iterator<Item = Result<StoreId>> + 'a>, &'a Store);
impl<'a, E> $itername<'a, E>
where E: From<StoreError>
impl<'a> $itername<'a>
{
pub fn new(inner: Box<Iterator<Item = RResult<StoreId, E>> + 'a>, store: &'a Store) -> Self {
pub fn new(inner: Box<Iterator<Item = Result<StoreId>> + 'a>, store: &'a Store) -> Self {
$itername(inner, store)
}
}
impl<'a, E> Iterator for $itername<'a, E>
where E: From<StoreError>
impl<'a> Iterator for $itername<'a>
{
type Item = RResult<$yield, E>;
type Item = Result<$yield>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|id| $fun(id?, self.1).map_err(E::from))
self.0.next().map(|id| $fun(id?, self.1))
}
}
pub trait $extname<'a, E>
where E: From<StoreError>
{
fn $extfnname(self, store: &'a Store) -> $itername<'a, E>;
pub trait $extname<'a> {
fn $extfnname(self, store: &'a Store) -> $itername<'a>;
}
impl<'a, I, E> $extname<'a, E> for I
where I: Iterator<Item = RResult<StoreId, E>> + 'a,
E: From<StoreError>
impl<'a, I> $extname<'a> for I
where I: Iterator<Item = Result<StoreId>> + 'a
{
fn $extfnname(self, store: &'a Store) -> $itername<'a, E> {
fn $extfnname(self, store: &'a Store) -> $itername<'a> {
$itername(Box::new(self), store)
}
}
@ -151,8 +144,7 @@ use self::get::StoreGetIterator;
use self::retrieve::StoreRetrieveIterator;
use file_abstraction::iter::PathIterator;
use store::Store;
use error::StoreError;
use error::Result;
use failure::Fallible as Result;
/// Iterator for iterating over all (or a subset of all) entries
///
@ -194,21 +186,21 @@ impl<'a> Entries<'a> {
/// Transform the iterator into a StoreDeleteIterator
///
/// This immitates the API from `libimagstore::iter`.
pub fn into_delete_iter(self) -> StoreDeleteIterator<'a, StoreError> {
pub fn into_delete_iter(self) -> StoreDeleteIterator<'a> {
StoreDeleteIterator::new(Box::new(self.0), self.1)
}
/// Transform the iterator into a StoreGetIterator
///
/// This immitates the API from `libimagstore::iter`.
pub fn into_get_iter(self) -> StoreGetIterator<'a, StoreError> {
pub fn into_get_iter(self) -> StoreGetIterator<'a> {
StoreGetIterator::new(Box::new(self.0), self.1)
}
/// Transform the iterator into a StoreRetrieveIterator
///
/// This immitates the API from `libimagstore::iter`.
pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a, StoreError> {
pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a> {
StoreRetrieveIterator::new(Box::new(self.0), self.1)
}

View file

@ -44,7 +44,7 @@ extern crate semver;
extern crate walkdir;
#[macro_use] extern crate is_match;
extern crate serde_json;
#[macro_use] extern crate error_chain;
#[macro_use] extern crate failure;
extern crate toml_query;
extern crate libimagerror;
@ -53,7 +53,6 @@ extern crate libimagutil;
#[macro_use] mod util;
pub mod storeid;
pub mod error;
pub mod iter;
pub mod store;
mod configuration;

View file

@ -31,12 +31,16 @@ use std::fmt::Formatter;
use std::fmt::Debug;
use std::fmt::Error as FMTError;
use libimagerror::errors::ErrorMsg as EM;
use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::read::TomlValueReadTypeExt;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::err_msg;
use failure::Error;
use error::{StoreError as SE, StoreErrorKind as SEK};
use error::ResultExt;
use storeid::{IntoStoreId, StoreId};
use iter::Entries;
use file_abstraction::FileAbstractionInstance;
@ -48,9 +52,6 @@ pub use file_abstraction::InMemoryFileAbstraction;
use libimagutil::debug_result::*;
/// The Result Type returned by any interaction with the store that could fail
pub type Result<T> = RResult<T, SE>;
#[derive(Debug, PartialEq)]
enum StoreEntryStatus {
@ -76,7 +77,7 @@ impl StoreEntry {
{
open_file(pb.clone())
.and_then(|f| f.lock_exclusive())
.chain_err(|| SEK::IoError)?;
.with_context(|| EM::IO)?;
}
Ok(StoreEntry {
@ -94,15 +95,12 @@ impl StoreEntry {
fn get_entry(&mut self) -> Result<Entry> {
if !self.is_borrowed() {
self.file
.get_file_content(self.id.clone())
.or_else(|err| if is_match!(err.kind(), &SEK::FileNotFound) {
Ok(Entry::new(self.id.clone()))
} else {
Err(err)
})
match self.file.get_file_content(self.id.clone())? {
Some(file) => Ok(file),
None => Ok(Entry::new(self.id.clone()))
}
} else {
Err(SE::from_kind(SEK::EntryAlreadyBorrowed(self.id.clone())))
Err(format_err!("EntryAlreadyBorrowed: {}", self.id))
}
}
@ -184,18 +182,19 @@ impl Store {
debug!("Building new Store object");
if !location.exists() {
if !config_implicit_store_create_allowed(store_config)? {
return Err(SE::from_kind(SEK::CreateStoreDirDenied))
.chain_err(|| SEK::FileError)
.chain_err(|| SEK::IoError);
return Err(format_err!("CreateStoreDirDenied"))
.context(EM::FileError)
.context(EM::IO)
.map_err(Error::from)
}
backend
.create_dir_all(&location)
.chain_err(|| SEK::StorePathCreate(location.clone()))
.context(format_err!("StorePathCreate: {}", location.display()))
.map_dbg_err_str("Failed")?;
} else if location.is_file() {
debug!("Store path exists as file");
return Err(SE::from_kind(SEK::StorePathExists(location)));
return Err(format_err!("StorePathExists: {}", location.display()));
}
let store = Store {
@ -218,45 +217,34 @@ impl Store {
///
/// On success: FileLockEntry
///
/// On error:
/// - Errors StoreId::into_storeid() might return
/// - EntryAlreadyExists(id) if the entry exists
/// - CreateCallError(LockPoisoned()) if the internal lock is poisened.
/// - CreateCallError(EntryAlreadyExists()) if the entry exists already.
///
pub fn create<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
let id = id.into_storeid()?.with_base(self.path().clone());
debug!("Creating id: '{}'", id);
let exists = self.entries
let exists = id.exists()? || self.entries
.read()
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
.map(|map| map.contains_key(&id))
.and_then(|exists| if exists {
Ok(exists)
} else {
let pb = id.clone().into_pathbuf().map_err(SE::from)?;
self.backend.exists(&pb)
})
.chain_err(|| SEK::CreateCallError(id.clone()))?;
.map_err(|_| Error::from(EM::LockError))
.context(format_err!("CreateCallError: {}", id))?;
if exists {
debug!("Entry exists: {:?}", id);
return Err(SEK::EntryAlreadyExists(id).into());
return Err(format_err!("EntryAlreadyExists: {}", id));
}
{
let mut hsmap = self
.entries
.write()
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
.chain_err(|| SEK::CreateCallError(id.clone()))?;
.map_err(|_| Error::from(EM::LockError))
.context(format_err!("CreateCallError: {}", id))?;
if hsmap.contains_key(&id) {
debug!("Cannot create, internal cache already contains: '{}'", id);
return Err(SE::from_kind(SEK::EntryAlreadyExists(id.clone())))
.chain_err(|| SEK::CreateCallError(id.clone()));
return Err(format_err!("EntryAlreadyExists: {}", id))
.context(format_err!("CreateCallError: {}", id))
.map_err(Error::from)
}
hsmap.insert(id.clone(), {
debug!("Creating: '{}'", id);
@ -281,17 +269,13 @@ impl Store {
///
/// On success: FileLockEntry
///
/// On error:
/// - Errors StoreId::into_storeid() might return
/// - RetrieveCallError(LockPoisoned()) if the internal lock is poisened.
///
pub fn retrieve<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
let id = id.into_storeid()?.with_base(self.path().clone());
debug!("Retrieving id: '{}'", id);
let entry = self
.entries
.write()
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
.map_err(|_| Error::from(EM::LockError))
.and_then(|mut es| {
let new_se = StoreEntry::new(id.clone(), &self.backend)?;
let se = es.entry(id.clone()).or_insert(new_se);
@ -299,7 +283,7 @@ impl Store {
se.status = StoreEntryStatus::Borrowed;
entry
})
.chain_err(|| SEK::RetrieveCallError(id.clone()))?;
.context(format_err!("RetrieveCallError: {}", id))?;
debug!("Constructing FileLockEntry: '{}'", id);
Ok(FileLockEntry::new(self, entry))
@ -320,24 +304,21 @@ impl Store {
debug!("Getting id: '{}'", id);
let exists = self.entries
let exists = id.exists()? || self.entries
.read()
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
.map(|map| map.contains_key(&id))
.and_then(|exists| if exists {
Ok(exists)
} else {
let pb = id.clone().into_pathbuf().map_err(SE::from)?;
self.backend.exists(&pb)
})
.chain_err(|| SEK::GetCallError(id.clone()))?;
.map_err(|_| Error::from(EM::LockError))
.context(format_err!("GetCallError: {}", id))?;
if !exists {
debug!("Does not exist in internal cache or filesystem: {:?}", id);
return Ok(None);
}
self.retrieve(id.clone()).map(Some).chain_err(|| SEK::GetCallError(id))
self.retrieve(id.clone())
.map(Some)
.context(format_err!("GetCallError: {}", id))
.map_err(Error::from)
}
/// Write (update) the `FileLockEntry` to disk
@ -346,15 +327,11 @@ impl Store {
///
/// On success: Entry
///
/// On error:
/// - UpdateCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
/// - IdNotFound() if the entry was not found in the stor
/// - Errors Entry::verify() might return
/// - Errors StoreEntry::write_entry() might return
///
pub fn update<'a>(&'a self, entry: &mut FileLockEntry<'a>) -> Result<()> {
debug!("Updating FileLockEntry at '{}'", entry.get_location());
self._update(entry, false).chain_err(|| SEK::UpdateCallError(entry.get_location().clone()))
self._update(entry, false)
.context(format_err!("UpdateCallError: {}", entry.get_location()))
.map_err(Error::from)
}
/// Internal method to write to the filesystem store.
@ -365,10 +342,11 @@ impl Store {
/// it is not public.
///
fn _update<'a>(&'a self, entry: &mut FileLockEntry<'a>, modify_presence: bool) -> Result<()> {
let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
let mut hsmap = self.entries.write()
.map_err(|_| Error::from(EM::LockError))?;
let se = hsmap.get_mut(&entry.location).ok_or_else(|| {
SE::from_kind(SEK::IdNotFound(entry.location.clone()))
EM::EntryNotFound(entry.location.local_display_string())
})?;
assert!(se.is_borrowed(), "Tried to update a non borrowed entry.");
@ -401,7 +379,8 @@ impl Store {
pub fn flush_cache(&self) -> Result<()> {
// We borrow this early so that between the aggregation of the flushables and the actual
// flush, there is no borrowing from the store.
let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
let mut hsmap = self.entries.write()
.map_err(|_| Error::from(EM::LockError))?;
let mut to_flush = vec![];
for (storeid, se) in hsmap.deref() {
@ -421,37 +400,34 @@ impl Store {
/// The number of elements in the internal cache
pub fn cache_size(&self) -> Result<usize> {
let hsmap = self.entries.read().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
let hsmap = self.entries.read().map_err(|_| Error::from(EM::LockError))?;
Ok(hsmap.iter().count())
}
/// The size of the internal cache
pub fn cache_capacity(&self) -> Result<usize> {
let hsmap = self.entries.read().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
let hsmap = self.entries.read().map_err(|_| Error::from(EM::LockError))?;
Ok(hsmap.capacity())
}
/// Get a copy of a given entry, this cannot be used to mutate the one on disk
// Get a copy of a given entry, this cannot be used to mutate the one on disk
///
/// # Return value
///
/// On success: Entry
///
/// On error:
/// - RetrieveCopyCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
/// - RetrieveCopyCallError(IdLocked()) if the Entry is borrowed currently
/// - Errors StoreEntry::new() might return
///
pub fn get_copy<S: IntoStoreId>(&self, id: S) -> Result<Entry> {
let id = id.into_storeid()?.with_base(self.path().clone());
debug!("Retrieving copy of '{}'", id);
let entries = self.entries.write()
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
.chain_err(|| SEK::RetrieveCopyCallError(id.clone()))?;
.map_err(|_| Error::from(EM::LockError))
.context(format_err!("RetrieveCopyCallError: {}", id))?;
// if the entry is currently modified by the user, we cannot drop it
if entries.get(&id).map(|e| e.is_borrowed()).unwrap_or(false) {
return Err(SE::from_kind(SEK::IdLocked)).chain_err(|| SEK::RetrieveCopyCallError(id));
return Err(EM::IdLocked)
.context(format_err!("RetrieveCopyCallError: {}", id))
.map_err(Error::from)
}
StoreEntry::new(id, &self.backend)?.get_entry()
@ -463,27 +439,29 @@ impl Store {
///
/// On success: ()
///
/// On error:
/// - DeleteCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
/// - DeleteCallError(FileNotFound()) if the StoreId refers to a non-existing entry.
/// - DeleteCallError(FileError()) if the internals failed to remove the file.
///
pub fn delete<S: IntoStoreId>(&self, id: S) -> Result<()> {
let id = id.into_storeid()?.with_base(self.path().clone());
debug!("Deleting id: '{}'", id);
// Small optimization: We need the pathbuf for deleting, but when calling
// StoreId::exists(), a PathBuf object gets allocated. So we simply get a
// PathBuf here, check whether it is there and if it is, we can re-use it to
// delete the filesystem file.
let pb = id.clone().into_pathbuf()?;
{
let mut entries = self
.entries
.write()
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
.chain_err(|| SEK::DeleteCallError(id.clone()))?;
.map_err(|_| Error::from(EM::LockError))
.context(format_err!("DeleteCallError: {}", id))?;
let do_remove = match entries.get(&id) {
Some(e) => if e.is_borrowed() { // entry is currently borrowed, we cannot delete it
return Err(SE::from_kind(SEK::IdLocked)).chain_err(|| SEK::DeleteCallError(id));
return Err(Error::from(EM::LockError))
.context(format_err!("DeleteCallError: {}", id))
.map_err(Error::from)
// false
} else { // Entry is in the cache
// Remove Entry from the cache
@ -496,8 +474,9 @@ impl Store {
if !self.backend.exists(&pb)? {
debug!("Seems like {:?} is not even on the FS", pb);
return Err(SE::from_kind(SEK::FileNotFound))
.chain_err(|| SEK::DeleteCallError(id))
return Err(EM::FileNotFound)
.context(format_err!("DeleteCallError: {}", id))
.map_err(Error::from)
} // else { continue }
false
@ -513,8 +492,8 @@ impl Store {
let _ = self
.backend
.remove_file(&pb)
.chain_err(|| SEK::FileError)
.chain_err(|| SEK::DeleteCallError(id))?;
.context(EM::FileError)
.context(format_err!("DeleteCallError: {}", id))?;
debug!("Deleted");
Ok(())
@ -540,12 +519,13 @@ impl Store {
let hsmap = self
.entries
.write()
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
.chain_err(|| SEK::MoveCallError(entry.get_location().clone(), new_id.clone()))?;
.map_err(|_| Error::from(EM::LockError))
.context(format_err!("MoveCallError: {} -> {}", entry.get_location(), new_id))?;
if hsmap.contains_key(&new_id) {
return Err(SE::from_kind(SEK::EntryAlreadyExists(new_id.clone())))
.chain_err(|| SEK::MoveCallError(entry.get_location().clone(), new_id.clone()))
return Err(format_err!("Entry exists already: {}", new_id.clone()))
.context(format_err!("MoveCallError: {} -> {}", entry.get_location(), new_id))
.map_err(Error::from)
}
let old_id = entry.get_location().clone();
@ -560,8 +540,9 @@ impl Store {
} else {
Ok(())
})
.chain_err(|| SEK::FileError)
.chain_err(|| SEK::MoveCallError(old_id, new_id))
.context(EM::FileError)
.context(format_err!("MoveCallError: {} -> {}", old_id, new_id))
.map_err(Error::from)
}
/// Move an entry without loading
@ -604,10 +585,11 @@ impl Store {
debug!("Moving '{}' to '{}'", old_id, new_id);
{
let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
let mut hsmap = self.entries.write()
.map_err(|_| Error::from(EM::LockError))?;
if hsmap.contains_key(&new_id) {
return Err(SE::from_kind(SEK::EntryAlreadyExists(new_id.clone())));
return Err(format_err!("Entry already exists: {}", new_id));
}
debug!("New id does not exist in cache");
@ -615,7 +597,7 @@ impl Store {
// if we have one, but it is borrowed, we really should not rename it, as this might
// lead to strange errors
if hsmap.get(&old_id).map(|e| e.is_borrowed()).unwrap_or(false) {
return Err(SE::from_kind(SEK::EntryAlreadyBorrowed(old_id.clone())));
return Err(format_err!("Entry already borrowed: {}", old_id));
}
debug!("Old id is not yet borrowed");
@ -624,14 +606,18 @@ impl Store {
let new_id_pb = new_id.clone().with_base(self.path().clone()).into_pathbuf()?;
if self.backend.exists(&new_id_pb)? {
return Err(SE::from_kind(SEK::EntryAlreadyExists(new_id.clone())));
return Err(format_err!("Entry already exists: {}", new_id));
}
debug!("New entry does not yet exist on filesystem. Good.");
let _ = self
.backend
.rename(&old_id_pb, &new_id_pb)
.chain_err(|| SEK::EntryRenameError(old_id_pb, new_id_pb))?;
.context({
let old = old_id_pb.display().to_string();
let new = new_id_pb.display().to_string();
format_err!("Rename error: {} -> {}", old, new)
})?;
debug!("Rename worked on filesystem");
@ -792,7 +778,7 @@ impl Entry {
pub fn from_reader<S: IntoStoreId>(loc: S, file: &mut Read) -> Result<Entry> {
let text = {
let mut s = String::new();
file.read_to_string(&mut s)?;
file.read_to_string(&mut s).context(EM::IO)?;
s
};
Self::from_str(loc, &text[..])
@ -828,7 +814,9 @@ impl Entry {
/// disk).
pub fn to_str(&self) -> Result<String> {
Ok(format!("---\n{header}---\n{content}",
header = ::toml::ser::to_string_pretty(&self.header)?,
header = ::toml::ser::to_string_pretty(&self.header)
.map_err(Error::from)
.context(err_msg("TOML Error"))?,
content = self.content))
}
@ -872,12 +860,12 @@ impl Entry {
/// Currently, this only verifies the header. This might change in the future.
pub fn verify(&self) -> Result<()> {
if !has_main_section(&self.header)? {
Err(SE::from_kind(SEK::MissingMainSection))
Err(format_err!("MissingMainSection"))
} else if !has_imag_version_in_main_section(&self.header)? {
Err(SE::from_kind(SEK::MissingVersionInfo))
Err(format_err!("MissingVersionInfo"))
} else if !has_only_tables(&self.header)? {
debug!("Could not verify that it only has tables in its base table");
Err(SE::from_kind(SEK::NonTableInBaseTable))
Err(format_err!("NonTableInBaseTable"))
} else {
Ok(())
}
@ -899,21 +887,26 @@ fn has_only_tables(t: &Value) -> Result<bool> {
debug!("Verifying that table has only tables");
match *t {
Value::Table(ref tab) => Ok(tab.iter().all(|(_, x)| is_match!(*x, Value::Table(_)))),
_ => Err(SE::from_kind(SEK::HeaderTypeFailure)),
_ => Err(format_err!("HeaderTypeFailure")),
}
}
fn has_main_section(t: &Value) -> Result<bool> {
t.read("imag")?
.ok_or_else(|| SE::from_kind(SEK::ConfigKeyMissingError("imag")))
t.read("imag")
.map_err(Error::from)
.context(EM::TomlQueryError)?
.ok_or_else(|| format_err!("ConfigKeyMissingError('imag')"))
.map(Value::is_table)
}
fn has_imag_version_in_main_section(t: &Value) -> Result<bool> {
t.read_string("imag.version")?
.ok_or_else(|| SE::from_kind(SEK::ConfigKeyMissingError("imag.version")))
t.read_string("imag.version")
.map_err(Error::from)
.context(EM::TomlQueryError)?
.ok_or_else(|| format_err!("ConfigKeyMissingError('imag.version')"))
.map_err(Error::from)
.map(String::from)
.map(|s| ::semver::Version::parse(&s).is_ok())
.map(|s: String| ::semver::Version::parse(&s).is_ok())
}
@ -1095,14 +1088,12 @@ mod store_tests {
#[test]
fn test_store_create_twice() {
use error::StoreErrorKind as SEK;
let store = get_store();
for n in 1..100 {
let s = format!("test-{}", n % 50);
store.create(PathBuf::from(s.clone()))
.map_err(|e| assert!(is_match!(e.kind(), &SEK::EntryAlreadyExists(_)) && n >= 50))
.ok()
.map(|entry| {
assert!(entry.verify().is_ok());
@ -1190,8 +1181,8 @@ mod store_tests {
assert!(store.create(id.clone()).is_ok());
}
let id_with_base = id.clone().with_base(store.path().clone());
{
let id_with_base = id.clone().with_base(store.path().clone());
assert!(store.entries.read().unwrap().get(&id_with_base).is_some());
}
@ -1203,16 +1194,8 @@ mod store_tests {
assert!(store.entries.read().unwrap().get(&id_mv_with_base).is_some());
}
{
let pb = id_with_base.into_pathbuf().unwrap();
let exists = store.backend.exists(&pb).unwrap();
assert!(!exists, "Old entry exists in Filesystem, but shouldn't");
}
let result = store.get(id.clone());
assert!(match result { Ok(None) => true, _ => false },
"Moved id ({:?}) is still there: {:?}", id, result);
assert!(match store.get(id.clone()) { Ok(None) => true, _ => false },
"Moved id ({:?}) is still there", id);
assert!(match store.get(id_mv.clone()) { Ok(Some(_)) => true, _ => false },
"New id ({:?}) is not in store...", id_mv);
}

View file

@ -26,10 +26,11 @@ use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;
use error::StoreErrorKind as SEK;
use error::StoreError as SE;
use error::ResultExt;
use store::Result;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::err_msg;
use failure::Error;
use store::Store;
use iter::create::StoreCreateIterator;
@ -64,14 +65,13 @@ impl StoreId {
///
/// Automatically creates a StoreId object which has a `base` set to `store_part` if stripping
/// the `store_part` from the `full_path` succeeded.
///
/// Returns a `StoreErrorKind::StoreIdBuildFromFullPathError` if stripping failes.
pub fn from_full_path<D>(store_part: &PathBuf, full_path: D) -> Result<StoreId>
where D: Deref<Target = Path>
{
let p = full_path
.strip_prefix(store_part)
.chain_err(|| SEK::StoreIdBuildFromFullPathError)?;
.map_err(Error::from)
.context(err_msg("Error building Store Id from full path"))?;
StoreId::new(Some(store_part.clone()), PathBuf::from(p))
}
@ -79,7 +79,7 @@ impl StoreId {
debug!("Trying to get a new baseless id from: {:?}", id);
if id.is_absolute() {
debug!("Error: Id is absolute!");
Err(SE::from_kind(SEK::StoreIdLocalPartAbsoluteError(id)))
Err(format_err!("Store Id local part is absolute: {}", id.display()))
} else {
debug!("Building Storeid object baseless");
Ok(StoreId {
@ -103,7 +103,9 @@ impl StoreId {
/// specified.
pub fn into_pathbuf(mut self) -> Result<PathBuf> {
let base = self.base.take();
let mut base = base.ok_or_else(|| SEK::StoreIdHasNoBaseError(self.id.clone()))?;
let mut base = base.ok_or_else(|| {
format_err!("Store Id has no base: {:?}", self.id.display().to_string())
})?;
base.push(self.id);
Ok(base)
}
@ -125,7 +127,8 @@ impl StoreId {
.unwrap_or_else(|| self.id.clone())
.to_str()
.map(String::from)
.ok_or_else(|| SE::from_kind(SEK::StoreIdHandlingError))
.ok_or_else(|| err_msg("Store ID Handling error"))
.map_err(Error::from)
}
/// Helper function for creating a displayable String from StoreId
@ -232,7 +235,7 @@ macro_rules! module_entry_path_mod {
use std::path::PathBuf;
use $crate::storeid::StoreId;
use $crate::store::Result;
use failure::Fallible as Result;
/// A Struct giving you the ability to choose store entries assigned
/// to it.
@ -313,8 +316,6 @@ impl<'a> Iterator for StoreIdIteratorWithStore<'a> {
}
}
use error::StoreError;
impl<'a> StoreIdIteratorWithStore<'a> {
pub fn new(iter: Box<Iterator<Item = Result<StoreId>>>, store: &'a Store) -> Self {
@ -328,7 +329,7 @@ impl<'a> StoreIdIteratorWithStore<'a> {
/// Transform the iterator into a StoreCreateIterator
///
/// This immitates the API from `libimagstore::iter`.
pub fn into_create_iter(self) -> StoreCreateIterator<'a, StoreError> {
pub fn into_create_iter(self) -> StoreCreateIterator<'a> {
StoreCreateIterator::new(Box::new(self.0), self.1)
}
@ -336,7 +337,7 @@ impl<'a> StoreIdIteratorWithStore<'a> {
///
///
/// This immitates the API from `libimagstore::iter`.
pub fn into_delete_iter(self) -> StoreDeleteIterator<'a, StoreError> {
pub fn into_delete_iter(self) -> StoreDeleteIterator<'a> {
StoreDeleteIterator::new(Box::new(self.0), self.1)
}
@ -344,7 +345,7 @@ impl<'a> StoreIdIteratorWithStore<'a> {
///
///
/// This immitates the API from `libimagstore::iter`.
pub fn into_get_iter(self) -> StoreGetIterator<'a, StoreError> {
pub fn into_get_iter(self) -> StoreGetIterator<'a> {
StoreGetIterator::new(Box::new(self.0), self.1)
}
@ -352,7 +353,7 @@ impl<'a> StoreIdIteratorWithStore<'a> {
///
///
/// This immitates the API from `libimagstore::iter`.
pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a, StoreError> {
pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a> {
StoreRetrieveIterator::new(Box::new(self.0), self.1)
}
@ -364,7 +365,6 @@ mod test {
use storeid::StoreId;
use storeid::IntoStoreId;
use error::StoreErrorKind as SEK;
module_entry_path_mod!("test");
@ -444,8 +444,6 @@ mod test {
let pb = id.unwrap().into_pathbuf();
assert!(pb.is_err());
assert!(is_match!(pb.unwrap_err().kind(), &SEK::StoreIdHasNoBaseError(_)));
}
#[test]

View file

@ -20,8 +20,10 @@
use std::fmt::Write;
use toml::Value;
use failure::Fallible as Result;
use failure::ResultExt;
use store::Result;
use libimagerror::errors::ErrorMsg as EM;
#[cfg(feature = "early-panic")]
#[macro_export]
@ -40,6 +42,7 @@ macro_rules! if_cfg_panic {
}
pub fn entry_buffer_to_header_content(buf: &str) -> Result<(Value, String)> {
debug!("Building entry from string");
let mut header = String::new();
let mut content = String::new();
@ -52,15 +55,16 @@ pub fn entry_buffer_to_header_content(buf: &str) -> Result<(Value, String)> {
header_consumed = true;
// do not further process the line
} else if !header_consumed {
let _ = writeln!(header, "{}", line)?;
let _ = writeln!(header, "{}", line).context(EM::FormatError)?;
} else if iter.peek().is_some() {
let _ = writeln!(content, "{}", line)?;
let _ = writeln!(content, "{}", line).context(EM::FormatError)?;
} else {
let _ = write!(content, "{}", line)?;
let _ = write!(content, "{}", line).context(EM::FormatError)?;
}
}
::toml::de::from_str(&header).map_err(From::from).map(|h| (h, content))
let h = ::toml::de::from_str(&header).context(EM::TomlDeserError)?;
Ok((h, content))
}
#[cfg(test)]