Merge pull request #1200 from matthiasbeyer/refactor-matching-into-command-chaining
Refactor matching into command chaining
This commit is contained in:
commit
c0f4a20367
32 changed files with 377 additions and 475 deletions
|
@ -80,21 +80,18 @@ pub fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
|
|||
s
|
||||
};
|
||||
|
||||
match ::toml::de::from_str::<::toml::Value>(&content[..]) {
|
||||
Ok(res) => Some(res),
|
||||
Err(e) => {
|
||||
::toml::de::from_str::<::toml::Value>(&content[..])
|
||||
.map(Some)
|
||||
.unwrap_or_else(|e| {
|
||||
let line_col = e
|
||||
.line_col()
|
||||
.map(|(line, col)| {
|
||||
format!("Line {}, Column {}", line, col)
|
||||
})
|
||||
.map(|(line, col)| format!("Line {}, Column {}", line, col))
|
||||
.unwrap_or_else(|| String::from("Line unknown, Column unknown"));
|
||||
|
||||
let _ = write!(stderr(), "Config file parser error at {}", line_col);
|
||||
trace_error(&e);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.nth(0)
|
||||
.ok_or(RE::from_kind(REK::ConfigNoConfigFileFound))
|
||||
|
@ -114,27 +111,20 @@ pub fn override_config(val: &mut Value, v: Vec<String>) -> Result<()> {
|
|||
|
||||
let iter = v.into_iter()
|
||||
.map(|s| { debug!("Trying to process '{}'", s); s })
|
||||
.filter_map(|s| match s.into_kv() {
|
||||
Some(kv) => Some(kv.into()),
|
||||
None => {
|
||||
warn!("Could split at '=' - will be ignore override");
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|(k, v)| val
|
||||
.read(&k[..])
|
||||
.chain_err(|| REK::ConfigTOMLParserError)
|
||||
.map(|toml| match toml {
|
||||
Some(value) => match into_value(value, v) {
|
||||
Some(v) => {
|
||||
info!("Successfully overridden: {} = {}", k, v);
|
||||
Ok(())
|
||||
},
|
||||
None => Err(RE::from_kind(REK::ConfigOverrideTypeNotMatching)),
|
||||
},
|
||||
None => Err(RE::from_kind(REK::ConfigOverrideKeyNotAvailable)),
|
||||
})
|
||||
);
|
||||
.filter_map(|s| s.into_kv().map(Into::into).or_else(|| {
|
||||
warn!("Could split at '=' - will be ignore override");
|
||||
None
|
||||
}))
|
||||
.map(|(k, v)| {
|
||||
let value = val
|
||||
.read(&k)
|
||||
.chain_err(|| REK::ConfigTOMLParserError)?
|
||||
.ok_or(RE::from_kind(REK::ConfigOverrideKeyNotAvailable))?;
|
||||
|
||||
into_value(value, v)
|
||||
.map(|v| info!("Successfully overridden: {} = {}", k, v))
|
||||
.ok_or_else(|| RE::from_kind(REK::ConfigOverrideTypeNotMatching))
|
||||
});
|
||||
|
||||
for elem in iter {
|
||||
let _ = try!(elem.chain_err(|| REK::ConfigOverrideError));
|
||||
|
|
|
@ -238,16 +238,16 @@ fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Value>)
|
|||
}
|
||||
|
||||
if let Some(cfg) = config {
|
||||
let cfg_loglevel = match cfg.read("imag.logging.level") {
|
||||
Ok(Some(&Value::String(ref s))) => match_log_level_str(s),
|
||||
Ok(Some(_)) => {
|
||||
let cfg_loglevel = cfg
|
||||
.read("imag.logging.level")?
|
||||
.ok_or(RE::from_kind(EK::GlobalLogLevelConfigMissing))?
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
let path = "imag.logging.level".to_owned();
|
||||
let ty = "String";
|
||||
return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
|
||||
},
|
||||
Ok(None) => return Err(RE::from_kind(EK::GlobalLogLevelConfigMissing)),
|
||||
Err(e) => return Err(e).map_err(From::from),
|
||||
}?;
|
||||
RE::from_kind(EK::ConfigTypeError(path, ty))
|
||||
})
|
||||
.and_then(match_log_level_str)?;
|
||||
|
||||
if let Some(cli_loglevel) = get_arg_loglevel(matches)? {
|
||||
if cli_loglevel > cfg_loglevel {
|
||||
|
@ -285,14 +285,13 @@ fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
|
|||
raw.iter()
|
||||
.fold(Ok(vec![]), |acc, val| {
|
||||
acc.and_then(|mut v| {
|
||||
let dest = match *val {
|
||||
Value::String(ref s) => translate_destination(s)?,
|
||||
_ => {
|
||||
let dest = val.as_str()
|
||||
.ok_or_else(|| {
|
||||
let path = "imag.logging.modules.<mod>.destinations".to_owned();
|
||||
let ty = "Array<String>";
|
||||
return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
|
||||
},
|
||||
};
|
||||
RE::from_kind(EK::ConfigTypeError(path, ty))
|
||||
})
|
||||
.and_then(translate_destination)?;
|
||||
v.push(dest);
|
||||
Ok(v)
|
||||
})
|
||||
|
@ -304,16 +303,16 @@ fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
|
|||
{
|
||||
|
||||
match config {
|
||||
Some(cfg) => match cfg.read("imag.logging.destinations") {
|
||||
Ok(Some(&Value::Array(ref a))) => translate_destinations(a),
|
||||
Ok(Some(_)) => {
|
||||
Some(cfg) => cfg
|
||||
.read("imag.logging.destinations")?
|
||||
.ok_or_else(|| RE::from_kind(EK::GlobalDestinationConfigMissing))?
|
||||
.as_array()
|
||||
.ok_or_else(|| {
|
||||
let path = "imag.logging.destinations".to_owned();
|
||||
let ty = "Array";
|
||||
Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
|
||||
},
|
||||
Ok(None) => Err(RE::from_kind(EK::GlobalDestinationConfigMissing)),
|
||||
Err(e) => Err(e).map_err(From::from),
|
||||
},
|
||||
RE::from_kind(EK::ConfigTypeError(path, ty))
|
||||
})
|
||||
.and_then(translate_destinations),
|
||||
None => {
|
||||
if let Some(values) = matches.value_of(Runtime::arg_logdest_name()) {
|
||||
// parse logdest specification from commandline
|
||||
|
@ -334,12 +333,12 @@ fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
|
|||
|
||||
macro_rules! aggregate_global_format {
|
||||
($read_str:expr, $error_kind_if_missing:expr, $config:expr) => {
|
||||
match try!($config.ok_or(RE::from_kind($error_kind_if_missing))).read($read_str) {
|
||||
Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
|
||||
Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError($read_str.to_owned(), "String"))),
|
||||
Ok(None) => Err(RE::from_kind($error_kind_if_missing)),
|
||||
Err(e) => Err(e).map_err(From::from),
|
||||
}
|
||||
try!($config.ok_or(RE::from_kind($error_kind_if_missing)))
|
||||
.read($read_str)?
|
||||
.ok_or_else(|| RE::from_kind($error_kind_if_missing))?
|
||||
.as_str()
|
||||
.map(String::from)
|
||||
.ok_or_else(|| RE::from_kind(EK::ConfigTypeError($read_str.to_owned(), "String")))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -386,6 +385,18 @@ fn aggregate_global_format_error(config: Option<&Value>)
|
|||
fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
|
||||
-> Result<BTreeMap<ModuleName, ModuleSettings>>
|
||||
{
|
||||
// Helper macro to return the error from Some(Err(_)) and map everything else to an
|
||||
// Option<_>
|
||||
macro_rules! inner_try {
|
||||
($v:expr) => {
|
||||
match $v {
|
||||
Some(Ok(v)) => Some(v),
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match config {
|
||||
Some(cfg) => match cfg.read("imag.logging.modules") {
|
||||
Ok(Some(&Value::Table(ref t))) => {
|
||||
|
@ -393,35 +404,39 @@ fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
|
|||
let mut settings = BTreeMap::new();
|
||||
|
||||
for (module_name, v) in t {
|
||||
let destinations = match v.read("destinations")? {
|
||||
Some(&Value::Array(ref a)) => Some(translate_destinations(a)?),
|
||||
None => None,
|
||||
Some(_) => {
|
||||
let path = "imag.logging.modules.<mod>.destinations".to_owned();
|
||||
let ty = "Array";
|
||||
return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
|
||||
},
|
||||
let destinations = inner_try! {
|
||||
v.read("destinations")?
|
||||
.map(|val| {
|
||||
val.as_array()
|
||||
.ok_or_else(|| {
|
||||
let path = "imag.logging.modules.<mod>.destinations".to_owned();
|
||||
let ty = "Array";
|
||||
RE::from_kind(EK::ConfigTypeError(path, ty))
|
||||
})
|
||||
.and_then(translate_destinations)
|
||||
})
|
||||
};
|
||||
|
||||
let level = match v.read("level")? {
|
||||
Some(&Value::String(ref s)) => Some(match_log_level_str(s)?),
|
||||
None => None,
|
||||
Some(_) => {
|
||||
let path = "imag.logging.modules.<mod>.level".to_owned();
|
||||
let ty = "String";
|
||||
return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
|
||||
},
|
||||
let level = inner_try! {
|
||||
v.read("level")?
|
||||
.map(|val| {
|
||||
val.as_str()
|
||||
.ok_or_else(|| {
|
||||
let path = "imag.logging.modules.<mod>.level".to_owned();
|
||||
let ty = "String";
|
||||
RE::from_kind(EK::ConfigTypeError(path, ty))
|
||||
})
|
||||
.and_then(match_log_level_str)
|
||||
})
|
||||
};
|
||||
|
||||
let enabled = match v.read("enabled")? {
|
||||
Some(&Value::Boolean(b)) => b,
|
||||
None => false,
|
||||
Some(_) => {
|
||||
let enabled = v.read("enabled")?
|
||||
.map(|v| v.as_bool().unwrap_or(false))
|
||||
.ok_or_else(|| {
|
||||
let path = "imag.logging.modules.<mod>.enabled".to_owned();
|
||||
let ty = "Boolean";
|
||||
return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
|
||||
},
|
||||
};
|
||||
RE::from_kind(EK::ConfigTypeError(path, ty))
|
||||
})?;
|
||||
|
||||
let module_settings = ModuleSettings {
|
||||
enabled: enabled,
|
||||
|
|
|
@ -325,10 +325,11 @@ impl Store {
|
|||
debug!("Creating id: '{}'", id);
|
||||
|
||||
{
|
||||
let mut hsmap = match self.entries.write() {
|
||||
Err(_) => return Err(SE::from_kind(SEK::LockPoisoned)).chain_err(|| SEK::CreateCallError),
|
||||
Ok(s) => s,
|
||||
};
|
||||
let mut hsmap = self
|
||||
.entries
|
||||
.write()
|
||||
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
|
||||
.chain_err(|| SEK::CreateCallError)?;
|
||||
|
||||
if hsmap.contains_key(&id) {
|
||||
debug!("Cannot create, internal cache already contains: '{}'", id);
|
||||
|
@ -480,10 +481,7 @@ impl Store {
|
|||
/// - Errors StoreEntry::write_entry() might return
|
||||
///
|
||||
fn _update<'a>(&'a self, entry: &mut FileLockEntry<'a>, modify_presence: bool) -> Result<()> {
|
||||
let mut hsmap = match self.entries.write() {
|
||||
Err(_) => return Err(SE::from_kind(SEK::LockPoisoned)),
|
||||
Ok(e) => e,
|
||||
};
|
||||
let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
|
||||
|
||||
let se = hsmap.get_mut(&entry.location).ok_or_else(|| {
|
||||
SE::from_kind(SEK::IdNotFound(entry.location.clone()))
|
||||
|
@ -519,13 +517,9 @@ impl Store {
|
|||
pub fn retrieve_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 = match self.entries.write() {
|
||||
Err(_) => {
|
||||
return Err(SE::from_kind(SEK::LockPoisoned))
|
||||
.chain_err(|| SEK::RetrieveCopyCallError);
|
||||
},
|
||||
Ok(e) => e,
|
||||
};
|
||||
let entries = self.entries.write()
|
||||
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
|
||||
.chain_err(|| SEK::RetrieveCopyCallError)?;
|
||||
|
||||
// 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) {
|
||||
|
@ -552,11 +546,11 @@ impl Store {
|
|||
debug!("Deleting id: '{}'", id);
|
||||
|
||||
{
|
||||
let mut entries = match self.entries.write() {
|
||||
Err(_) => return Err(SE::from_kind(SEK::LockPoisoned))
|
||||
.chain_err(|| SEK::DeleteCallError),
|
||||
Ok(e) => e,
|
||||
};
|
||||
let mut entries = self
|
||||
.entries
|
||||
.write()
|
||||
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
|
||||
.chain_err(|| SEK::DeleteCallError)?;
|
||||
|
||||
// if the entry is currently modified by the user, we cannot drop it
|
||||
match entries.get(&id) {
|
||||
|
@ -588,11 +582,11 @@ impl Store {
|
|||
// remove the entry first, then the file
|
||||
entries.remove(&id);
|
||||
let pb = id.clone().with_base(self.path().clone()).into_pathbuf()?;
|
||||
if let Err(e) = self.backend.remove_file(&pb) {
|
||||
return Err(e)
|
||||
.chain_err(|| SEK::FileError)
|
||||
.chain_err(|| SEK::DeleteCallError);
|
||||
}
|
||||
let _ = self
|
||||
.backend
|
||||
.remove_file(&pb)
|
||||
.chain_err(|| SEK::FileError)
|
||||
.chain_err(|| SEK::DeleteCallError)?;
|
||||
}
|
||||
|
||||
debug!("Deleted");
|
||||
|
@ -631,14 +625,13 @@ impl Store {
|
|||
|
||||
let old_id_as_path = old_id.clone().with_base(self.path().clone()).into_pathbuf()?;
|
||||
let new_id_as_path = new_id.clone().with_base(self.path().clone()).into_pathbuf()?;
|
||||
self.backend.copy(&old_id_as_path, &new_id_as_path)
|
||||
.and_then(|_| {
|
||||
if remove_old {
|
||||
debug!("Removing old '{:?}'", old_id_as_path);
|
||||
self.backend.remove_file(&old_id_as_path)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
self.backend
|
||||
.copy(&old_id_as_path, &new_id_as_path)
|
||||
.and_then(|_| if remove_old {
|
||||
debug!("Removing old '{:?}'", old_id_as_path);
|
||||
self.backend.remove_file(&old_id_as_path)
|
||||
} else {
|
||||
Ok(())
|
||||
})
|
||||
.chain_err(|| SEK::FileError)
|
||||
.chain_err(|| SEK::MoveCallError)
|
||||
|
@ -684,10 +677,7 @@ impl Store {
|
|||
debug!("Moving '{}' to '{}'", old_id, new_id);
|
||||
|
||||
{
|
||||
let mut hsmap = match self.entries.write() {
|
||||
Err(_) => return Err(SE::from_kind(SEK::LockPoisoned)),
|
||||
Ok(m) => m,
|
||||
};
|
||||
let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
|
||||
|
||||
if hsmap.contains_key(&new_id) {
|
||||
return Err(SE::from_kind(SEK::EntryAlreadyExists(new_id.clone())));
|
||||
|
@ -711,22 +701,21 @@ impl Store {
|
|||
}
|
||||
debug!("New entry does not yet exist on filesystem. Good.");
|
||||
|
||||
match self.backend.rename(&old_id_pb, &new_id_pb) {
|
||||
Err(e) => return Err(e).chain_err(|| SEK::EntryRenameError(old_id_pb, new_id_pb)),
|
||||
Ok(_) => {
|
||||
debug!("Rename worked on filesystem");
|
||||
let _ = self
|
||||
.backend
|
||||
.rename(&old_id_pb, &new_id_pb)
|
||||
.chain_err(|| SEK::EntryRenameError(old_id_pb, new_id_pb))?;
|
||||
|
||||
// assert enforced through check hsmap.contains_key(&new_id) above.
|
||||
// Should therefor never fail
|
||||
assert!(hsmap
|
||||
.remove(&old_id)
|
||||
.and_then(|mut entry| {
|
||||
entry.id = new_id.clone();
|
||||
hsmap.insert(new_id.clone(), entry)
|
||||
}).is_none())
|
||||
}
|
||||
}
|
||||
debug!("Rename worked on filesystem");
|
||||
|
||||
// assert enforced through check hsmap.contains_key(&new_id) above.
|
||||
// Should therefor never fail
|
||||
assert!(hsmap
|
||||
.remove(&old_id)
|
||||
.and_then(|mut entry| {
|
||||
entry.id = new_id.clone();
|
||||
hsmap.insert(new_id.clone(), entry)
|
||||
}).is_none())
|
||||
}
|
||||
|
||||
debug!("Moved");
|
||||
|
@ -1035,16 +1024,11 @@ mod glob_store_iter {
|
|||
while let Some(o) = self.paths.next() {
|
||||
debug!("GlobStoreIdIterator::next() => {:?}", o);
|
||||
match o.chain_err(|| SEK::StoreIdHandlingError) {
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(path) => {
|
||||
if path.exists() && path.is_file() {
|
||||
return match StoreId::from_full_path(&self.store_path, path) {
|
||||
Ok(id) => Some(Ok(id)),
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
/* } else { */
|
||||
/* continue */
|
||||
}
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(path) => if path.exists() && path.is_file() {
|
||||
return Some(StoreId::from_full_path(&self.store_path, path));
|
||||
/* } else { */
|
||||
/* continue */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,13 +90,8 @@ impl<'a> BookmarkCollection<'a> {
|
|||
.and_then(|id| store.get(id))
|
||||
.chain_err(|| BEK::StoreReadError)
|
||||
.and_then(|fle| {
|
||||
match fle {
|
||||
None => Err(BE::from_kind(BEK::CollectionNotFound)),
|
||||
Some(e) => Ok(BookmarkCollection {
|
||||
fle: e,
|
||||
store: store,
|
||||
}),
|
||||
}
|
||||
fle.ok_or(BE::from_kind(BEK::CollectionNotFound))
|
||||
.map(|e| BookmarkCollection { fle: e, store: store })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ use error::*;
|
|||
use iter::HabitInstanceStoreIdIterator;
|
||||
use util::date_to_string;
|
||||
use util::IsHabitCheck;
|
||||
use util::get_string_header_from_entry;
|
||||
|
||||
use libimagentrylink::internal::InternalLinker;
|
||||
use libimagstore::store::Store;
|
||||
|
@ -202,27 +203,26 @@ impl HabitTemplate for Entry {
|
|||
}
|
||||
|
||||
fn habit_name(&self) -> Result<String> {
|
||||
get_string_header_from_habit(self, "habit.template.name")
|
||||
get_string_header_from_entry(self, "habit.template.name")
|
||||
}
|
||||
|
||||
fn habit_basedate(&self) -> Result<String> {
|
||||
get_string_header_from_habit(self, "habit.template.basedate")
|
||||
get_string_header_from_entry(self, "habit.template.basedate")
|
||||
}
|
||||
|
||||
fn habit_recur_spec(&self) -> Result<String> {
|
||||
get_string_header_from_habit(self, "habit.template.recurspec")
|
||||
get_string_header_from_entry(self, "habit.template.recurspec")
|
||||
}
|
||||
|
||||
fn habit_comment(&self) -> Result<String> {
|
||||
get_string_header_from_habit(self, "habit.template.comment")
|
||||
get_string_header_from_entry(self, "habit.template.comment")
|
||||
}
|
||||
|
||||
fn habit_until_date(&self) -> Result<Option<String>> {
|
||||
match self.get_header().read("habit.template.until")? {
|
||||
Some(&Value::String(ref s)) => Ok(Some(s.clone())),
|
||||
Some(_) => Err(HEK::HeaderTypeError("habit.template.until", "String").into()),
|
||||
None => Ok(None),
|
||||
}
|
||||
self.get_header()
|
||||
.read("habit.template.until")?
|
||||
.map(|v| v.as_str().map(String::from))
|
||||
.ok_or(HEK::HeaderTypeError("habit.template.until", "String").into())
|
||||
}
|
||||
|
||||
fn instance_id_for(habit_name: &String, habit_date: &NaiveDate) -> Result<StoreId> {
|
||||
|
@ -239,15 +239,6 @@ fn instance_id_for_name_and_datestr(habit_name: &String, habit_date: &String) ->
|
|||
.map_err(HE::from)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_string_header_from_habit(e: &Entry, path: &'static str) -> Result<String> {
|
||||
match e.get_header().read(path)? {
|
||||
Some(&Value::String(ref s)) => Ok(s.clone()),
|
||||
Some(_) => Err(HEK::HeaderTypeError(path, "String").into()),
|
||||
None => Err(HEK::HeaderFieldMissing(path).into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub mod builder {
|
||||
use toml::Value;
|
||||
use toml_query::insert::TomlValueInsertExt;
|
||||
|
|
|
@ -22,7 +22,6 @@ use toml::Value;
|
|||
use toml_query::read::TomlValueReadExt;
|
||||
use toml_query::set::TomlValueSetExt;
|
||||
|
||||
use error::HabitErrorKind as HEK;
|
||||
use error::*;
|
||||
use util::*;
|
||||
|
||||
|
@ -61,11 +60,8 @@ impl HabitInstance for Entry {
|
|||
}
|
||||
|
||||
fn get_date(&self) -> Result<NaiveDate> {
|
||||
match self.get_header().read("habit.instance.date")? {
|
||||
Some(&Value::String(ref s)) => date_from_string(s),
|
||||
Some(_) => Err(HEK::HeaderTypeError("habit.instance.date", "String").into()),
|
||||
None => Err(HEK::HeaderFieldMissing("habit.instance.date").into()),
|
||||
}
|
||||
use util::date_from_string;
|
||||
get_string_header_from_entry(self, "habit.instance.date").and_then(date_from_string)
|
||||
}
|
||||
|
||||
fn set_date(&mut self, n: &NaiveDate) -> Result<()> {
|
||||
|
@ -77,11 +73,7 @@ impl HabitInstance for Entry {
|
|||
}
|
||||
|
||||
fn get_comment(&self) -> Result<String> {
|
||||
match self.get_header().read("habit.instance.comment")? {
|
||||
Some(&Value::String(ref s)) => Ok(s.clone()),
|
||||
Some(_) => Err(HEK::HeaderTypeError("habit.instance.comment", "String").into()),
|
||||
None => Err(HEK::HeaderFieldMissing("habit.instance.comment").into()),
|
||||
}
|
||||
get_string_header_from_entry(self, "habit.instance.comment")
|
||||
}
|
||||
|
||||
fn set_comment(&mut self, c: String) -> Result<()> {
|
||||
|
@ -93,11 +85,7 @@ impl HabitInstance for Entry {
|
|||
}
|
||||
|
||||
fn get_template_name(&self) -> Result<String> {
|
||||
match self.get_header().read("habit.instance.name")? {
|
||||
Some(&Value::String(ref s)) => Ok(s.clone()),
|
||||
Some(_) => Err(HEK::HeaderTypeError("habit.instance.name", "String").into()),
|
||||
None => Err(HEK::HeaderFieldMissing("habit.instance.name").into()),
|
||||
}
|
||||
get_string_header_from_entry(self, "habit.instance.name")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ pub fn date_to_string(ndt: &NaiveDate) -> String {
|
|||
ndt.format(NAIVE_DATE_STRING_FORMAT).to_string()
|
||||
}
|
||||
|
||||
pub fn date_from_string(s: &str) -> Result<NaiveDate> {
|
||||
NaiveDate::parse_from_str(s, NAIVE_DATE_STRING_FORMAT).map_err(From::from)
|
||||
pub fn date_from_string(s: String) -> Result<NaiveDate> {
|
||||
NaiveDate::parse_from_str(&s, NAIVE_DATE_STRING_FORMAT).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Helper trait to check whether a object which can be a habit instance and a habit template is
|
||||
|
@ -90,3 +90,14 @@ impl IsHabitCheck for Entry {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_string_header_from_entry(e: &Entry, path: &'static str) -> Result<String> {
|
||||
use error::HabitErrorKind as HEK;
|
||||
use toml_query::read::TomlValueReadExt;
|
||||
|
||||
e.get_header()
|
||||
.read(path)?
|
||||
.ok_or(HEK::HeaderFieldMissing(path).into())
|
||||
.and_then(|o| o.as_str().map(String::from).ok_or(HEK::HeaderTypeError(path, "String").into()))
|
||||
}
|
||||
|
||||
|
|
|
@ -36,11 +36,10 @@ pub trait Log : DiaryEntry {
|
|||
impl Log for Entry {
|
||||
fn is_log(&self) -> Result<bool> {
|
||||
let location = "log.is_log";
|
||||
match self.get_header().read(location)? {
|
||||
Some(&Value::Boolean(b)) => Ok(b),
|
||||
Some(_) => Err(LE::from_kind(LEK::HeaderTypeError("boolean", location))),
|
||||
None => Ok(false)
|
||||
}
|
||||
self.get_header()
|
||||
.read(location)?
|
||||
.ok_or(LE::from_kind(LEK::HeaderTypeError("boolean", location)))
|
||||
.map(|v| v.as_bool().unwrap_or(false))
|
||||
}
|
||||
|
||||
fn make_log_entry(&mut self) -> Result<()> {
|
||||
|
|
|
@ -46,13 +46,12 @@ impl Note for Entry {
|
|||
}
|
||||
|
||||
fn get_name(&self) -> Result<String> {
|
||||
match self.get_header().read("note.name") {
|
||||
Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
|
||||
Ok(_) => {
|
||||
Err(NE::from_kind(NEK::HeaderTypeError)).chain_err(|| NEK::StoreReadError)
|
||||
},
|
||||
Err(e) => Err(e).chain_err(|| NEK::StoreReadError)
|
||||
}
|
||||
self.get_header()
|
||||
.read("note.name")
|
||||
.chain_err(|| NEK::StoreReadError)?
|
||||
.and_then(Value::as_str)
|
||||
.map(String::from)
|
||||
.ok_or(NE::from_kind(NEK::HeaderTypeError))
|
||||
}
|
||||
|
||||
fn set_text(&mut self, n: String) {
|
||||
|
|
|
@ -45,11 +45,10 @@ impl<'a> Iterator for GetTimeTrackIter<'a> {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(|sid| {
|
||||
match self.store.get(sid).chain_err(|| TTEK::StoreReadError) {
|
||||
Ok(None) => Err(TTE::from_kind(TTEK::StoreReadError)),
|
||||
Ok(Some(s)) => Ok(s),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
self.store
|
||||
.get(sid)
|
||||
.chain_err(|| TTEK::StoreReadError)?
|
||||
.ok_or(TTE::from_kind(TTEK::StoreReadError))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,11 +65,13 @@ impl TimeTracking for Entry {
|
|||
fn get_timetrack_tag(&self) -> Result<TTT> {
|
||||
self.get_header()
|
||||
.read(DATE_TIME_TAG_HEADER_PATH)
|
||||
.chain_err(|| TTEK::HeaderReadError)
|
||||
.and_then(|value| match value {
|
||||
Some(&Value::String(ref s)) => Ok(s.clone().into()),
|
||||
Some(_) => Err(TTE::from_kind(TTEK::HeaderFieldTypeError)),
|
||||
_ => Err(TTE::from_kind(TTEK::HeaderReadError))
|
||||
.chain_err(|| TTEK::HeaderReadError)?
|
||||
.ok_or(TTE::from_kind(TTEK::HeaderReadError))
|
||||
.and_then(|v| {
|
||||
v.as_str()
|
||||
.map(String::from)
|
||||
.map(Into::into)
|
||||
.ok_or(TTE::from_kind(TTEK::HeaderFieldTypeError))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ use error::Result;
|
|||
use libimagstore::store::Entry;
|
||||
|
||||
use uuid::Uuid;
|
||||
use toml::Value;
|
||||
use toml_query::read::TomlValueReadExt;
|
||||
|
||||
pub trait Task {
|
||||
|
@ -34,14 +33,13 @@ pub trait Task {
|
|||
|
||||
impl Task for Entry {
|
||||
fn get_uuid(&self) -> Result<Uuid> {
|
||||
match self.get_header().read("todo.uuid") {
|
||||
Ok(Some(&Value::String(ref uuid))) => {
|
||||
Uuid::parse_str(uuid).chain_err(|| TEK::UuidParserError)
|
||||
},
|
||||
Ok(Some(_)) => Err(TE::from_kind(TEK::HeaderTypeError)),
|
||||
Ok(None) => Err(TE::from_kind(TEK::HeaderFieldMissing)),
|
||||
Err(e) => Err(e).chain_err(|| TEK::StoreError),
|
||||
}
|
||||
self.get_header()
|
||||
.read("todo.uuid")
|
||||
.chain_err(|| TEK::StoreError)?
|
||||
.ok_or(TE::from_kind(TEK::HeaderFieldMissing))?
|
||||
.as_str()
|
||||
.ok_or(TE::from_kind(TEK::HeaderTypeError))
|
||||
.and_then(|u| Uuid::parse_str(u).chain_err(|| TEK::UuidParserError))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,13 +97,9 @@ impl Annotateable for Entry {
|
|||
|
||||
fn is_annotation(&self) -> Result<bool> {
|
||||
self.get_header()
|
||||
.read("annotation.is_annotation")
|
||||
.map_err(From::from)
|
||||
.and_then(|res| match res {
|
||||
Some(&Value::Boolean(b)) => Ok(b),
|
||||
None => Ok(false),
|
||||
_ => Err(AE::from_kind(AEK::HeaderTypeError)),
|
||||
})
|
||||
.read("annotation.is_annotation")?
|
||||
.map(|val| val.as_bool().unwrap_or(false))
|
||||
.ok_or(AE::from_kind(AEK::HeaderTypeError))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
use toml_query::insert::TomlValueInsertExt;
|
||||
use toml_query::read::TomlValueReadExt;
|
||||
use toml_query::error::ErrorKind as TQEK;
|
||||
use toml::Value;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
|
@ -81,17 +80,17 @@ impl EntryCategory for Entry {
|
|||
}
|
||||
|
||||
fn get_category(&self) -> Result<Option<Category>> {
|
||||
match self.get_header().read(&String::from("category.value")) {
|
||||
Err(res) => match res.kind() {
|
||||
&TQEK::IdentifierNotFoundInDocument(_) => Ok(None),
|
||||
_ => Err(res),
|
||||
}
|
||||
.chain_err(|| CEK::HeaderReadError),
|
||||
|
||||
Ok(Some(&Value::String(ref s))) => Ok(Some(s.clone().into())),
|
||||
Ok(None) => Err(CE::from_kind(CEK::StoreReadError)).chain_err(|| CEK::HeaderReadError),
|
||||
Ok(_) => Err(CE::from_kind(CEK::TypeError)).chain_err(|| CEK::HeaderReadError),
|
||||
}
|
||||
self.get_header()
|
||||
.read("category.value")
|
||||
.chain_err(|| CEK::HeaderReadError)
|
||||
.and_then(|opt| {
|
||||
opt.map(|v| {
|
||||
v.as_str()
|
||||
.map(String::from)
|
||||
.map(Category::from)
|
||||
})
|
||||
.ok_or(CE::from_kind(CEK::TypeError))
|
||||
})
|
||||
}
|
||||
|
||||
fn has_category(&self) -> Result<bool> {
|
||||
|
|
|
@ -22,6 +22,14 @@ error_chain! {
|
|||
CategoryError, CategoryErrorKind, ResultExt, Result;
|
||||
}
|
||||
|
||||
links {
|
||||
StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
|
||||
}
|
||||
|
||||
foreign_links {
|
||||
TomlQueryError(::toml_query::error::Error);
|
||||
}
|
||||
|
||||
errors {
|
||||
StoreReadError {
|
||||
description("Store Read error")
|
||||
|
|
|
@ -225,14 +225,13 @@ fn represents_category(store: &Store, sid: StoreId, name: &str) -> Result<bool>
|
|||
.chain_err(|| CEK::StoreReadError)
|
||||
.and_then(|fle| {
|
||||
if let Some(fle) = fle {
|
||||
match fle.get_header()
|
||||
fle.get_header()
|
||||
.read(&String::from(CATEGORY_REGISTER_NAME_FIELD_PATH))
|
||||
.chain_err(|| CEK::HeaderReadError)
|
||||
{
|
||||
Ok(Some(&Value::String(ref s))) => Ok(s == name),
|
||||
Ok(_) => Err(CE::from_kind(CEK::TypeError)),
|
||||
Err(e) => Err(e).chain_err(|| CEK::HeaderReadError),
|
||||
}
|
||||
.chain_err(|| CEK::HeaderReadError)?
|
||||
.ok_or(CE::from_kind(CEK::TypeError))?
|
||||
.as_str()
|
||||
.map(|s| s == name)
|
||||
.ok_or(CE::from_kind(CEK::TypeError))
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
|
@ -276,14 +275,15 @@ impl<'a> Iterator for CategoryNameIter<'a> {
|
|||
.next()
|
||||
.map(|sid| {
|
||||
self.0
|
||||
.get(sid)
|
||||
.chain_err(|| CEK::StoreReadError)
|
||||
.and_then(|fle| fle.ok_or(CE::from_kind(CEK::StoreReadError)))
|
||||
.and_then(|fle| match fle.get_header().read(&query) {
|
||||
Ok(Some(&Value::String(ref s))) => Ok(Category::from(s.clone())),
|
||||
Ok(_) => Err(CE::from_kind(CEK::TypeError)),
|
||||
Err(e) => Err(e).chain_err(|| CEK::HeaderReadError),
|
||||
})
|
||||
.get(sid)?
|
||||
.ok_or_else(|| CE::from_kind(CEK::StoreReadError))?
|
||||
.get_header()
|
||||
.read(&query)
|
||||
.chain_err(|| CEK::HeaderReadError)?
|
||||
.and_then(Value::as_str)
|
||||
.map(String::from)
|
||||
.map(Category::from)
|
||||
.ok_or_else(|| CE::from_kind(CEK::TypeError))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,12 +63,11 @@ impl EntryDate for Entry {
|
|||
.read(&DATE_HEADER_LOCATION)
|
||||
.chain_err(|| DEK::ReadDateError)
|
||||
.and_then(|v| {
|
||||
match v {
|
||||
Some(&Value::String(ref s)) => s.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError),
|
||||
Some(_) => Err(DE::from_kind(DEK::DateHeaderFieldTypeError)),
|
||||
_ => Err(DE::from_kind(DEK::ReadDateError)),
|
||||
}
|
||||
v.ok_or(DE::from_kind(DEK::ReadDateError))?
|
||||
.as_str()
|
||||
.ok_or(DE::from_kind(DEK::DateHeaderFieldTypeError))?
|
||||
.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -94,11 +93,10 @@ impl EntryDate for Entry {
|
|||
self.get_header_mut()
|
||||
.insert(&DATE_HEADER_LOCATION, Value::String(date))
|
||||
.map(|opt| opt.map(|stri| {
|
||||
match stri {
|
||||
Value::String(ref s) => s.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError),
|
||||
_ => Err(DE::from_kind(DEK::DateHeaderFieldTypeError)),
|
||||
}
|
||||
stri.as_str()
|
||||
.ok_or(DE::from_kind(DEK::DateHeaderFieldTypeError))?
|
||||
.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError)
|
||||
}))
|
||||
.chain_err(|| DEK::SetDateError)
|
||||
}
|
||||
|
@ -129,30 +127,15 @@ impl EntryDate for Entry {
|
|||
.get_header()
|
||||
.read(&DATE_RANGE_START_HEADER_LOCATION)
|
||||
.chain_err(|| DEK::ReadDateTimeRangeError)
|
||||
.and_then(|v| {
|
||||
match v {
|
||||
Some(&Value::String(ref s)) => s.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError),
|
||||
Some(_) => Err(DE::from_kind(DEK::DateHeaderFieldTypeError)),
|
||||
_ => Err(DE::from_kind(DEK::ReadDateError)),
|
||||
}
|
||||
})?;
|
||||
.and_then(|v| str_to_ndt(v.ok_or(DE::from_kind(DEK::ReadDateError))?))?;
|
||||
|
||||
let end = self
|
||||
.get_header()
|
||||
.read(&DATE_RANGE_START_HEADER_LOCATION)
|
||||
.chain_err(|| DEK::ReadDateTimeRangeError)
|
||||
.and_then(|v| {
|
||||
match v {
|
||||
Some(&Value::String(ref s)) => s.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError),
|
||||
Some(_) => Err(DE::from_kind(DEK::DateHeaderFieldTypeError)),
|
||||
_ => Err(DE::from_kind(DEK::ReadDateError)),
|
||||
}
|
||||
})?;
|
||||
.and_then(|v| str_to_ndt(v.ok_or(DE::from_kind(DEK::ReadDateError))?))?;
|
||||
|
||||
DateTimeRange::new(start, end)
|
||||
.chain_err(|| DEK::DateTimeRangeError)
|
||||
DateTimeRange::new(start, end).chain_err(|| DEK::DateTimeRangeError)
|
||||
}
|
||||
|
||||
/// Set the date range
|
||||
|
@ -171,25 +154,13 @@ impl EntryDate for Entry {
|
|||
let opt_old_start = self
|
||||
.get_header_mut()
|
||||
.insert(&DATE_RANGE_START_HEADER_LOCATION, Value::String(start))
|
||||
.map(|opt| opt.map(|stri| {
|
||||
match stri {
|
||||
Value::String(ref s) => s.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError),
|
||||
_ => Err(DE::from_kind(DEK::DateHeaderFieldTypeError)),
|
||||
}
|
||||
}))
|
||||
.map(|opt| opt.as_ref().map(str_to_ndt))
|
||||
.chain_err(|| DEK::SetDateTimeRangeError)?;
|
||||
|
||||
let opt_old_end = self
|
||||
.get_header_mut()
|
||||
.insert(&DATE_RANGE_END_HEADER_LOCATION, Value::String(end))
|
||||
.map(|opt| opt.map(|stri| {
|
||||
match stri {
|
||||
Value::String(ref s) => s.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError),
|
||||
_ => Err(DE::from_kind(DEK::DateHeaderFieldTypeError)),
|
||||
}
|
||||
}))
|
||||
.map(|opt| opt.as_ref().map(str_to_ndt))
|
||||
.chain_err(|| DEK::SetDateTimeRangeError)?;
|
||||
|
||||
match (opt_old_start, opt_old_end) {
|
||||
|
@ -210,6 +181,13 @@ impl EntryDate for Entry {
|
|||
|
||||
}
|
||||
|
||||
fn str_to_ndt(v: &Value) -> Result<NaiveDateTime> {
|
||||
v.as_str()
|
||||
.ok_or(DE::from_kind(DEK::DateHeaderFieldTypeError))?
|
||||
.parse::<NaiveDateTime>()
|
||||
.chain_err(|| DEK::DateTimeParsingError)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
|
|
@ -52,12 +52,7 @@ impl<P: Predicate> Filter<Entry> for FieldPredicate<P> {
|
|||
fn filter(&self, e: &Entry) -> bool {
|
||||
e.get_header()
|
||||
.read(&self.header_field_path[..])
|
||||
.map(|val| {
|
||||
match val {
|
||||
None => false,
|
||||
Some(v) => (*self.predicate).evaluate(v),
|
||||
}
|
||||
})
|
||||
.map(|val| val.map(|v| (*self.predicate).evaluate(v)).unwrap_or(false))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
//
|
||||
|
||||
use semver::Version;
|
||||
use toml::Value;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
|
||||
|
@ -44,15 +43,9 @@ impl Filter<Entry> for VersionEq {
|
|||
.read("imag.version")
|
||||
.map(|val| {
|
||||
val.map_or(false, |v| {
|
||||
match *v {
|
||||
Value::String(ref s) => {
|
||||
match Version::parse(&s[..]) {
|
||||
Ok(v) => v == self.version,
|
||||
_ => false
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
v.as_str()
|
||||
.map(|s| Version::parse(s).map(|v| v == self.version).unwrap_or(false))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
//
|
||||
|
||||
use semver::Version;
|
||||
use toml::Value;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
|
||||
|
@ -44,15 +43,9 @@ impl Filter<Entry> for VersionGt {
|
|||
.read("imag.version")
|
||||
.map(|val| {
|
||||
val.map_or(false, |v| {
|
||||
match *v {
|
||||
Value::String(ref s) => {
|
||||
match Version::parse(&s[..]) {
|
||||
Ok(v) => v > self.version,
|
||||
_ => false
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
v.as_str()
|
||||
.map(|s| Version::parse(s).map(|v| v > self.version).unwrap_or(false))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
//
|
||||
|
||||
use semver::Version;
|
||||
use toml::Value;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
|
||||
|
@ -44,15 +43,9 @@ impl Filter<Entry> for VersionLt {
|
|||
.read("imag.version")
|
||||
.map(|val| {
|
||||
val.map_or(false, |v| {
|
||||
match *v {
|
||||
Value::String(ref s) => {
|
||||
match Version::parse(&s[..]) {
|
||||
Ok(v) => v < self.version,
|
||||
_ => false
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
v.as_str()
|
||||
.map(|s| Version::parse(s).map(|v| v < self.version).unwrap_or(false))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|
|
|
@ -64,10 +64,9 @@ impl GPSEntry for Entry {
|
|||
}
|
||||
|
||||
fn get_coordinates(&self) -> Result<Option<Coordinates>> {
|
||||
match self.get_header().read("gps.coordinates").chain_err(|| GPSEK::HeaderWriteError) {
|
||||
Ok(Some(hdr)) => Coordinates::from_value(hdr).map(Some),
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
match self.get_header().read("gps.coordinates").chain_err(|| GPSEK::HeaderWriteError)? {
|
||||
Some(hdr) => Coordinates::from_value(hdr).map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,31 +78,27 @@ impl Into<Value> for GPSValue {
|
|||
|
||||
impl FromValue for GPSValue {
|
||||
fn from_value(v: &Value) -> Result<Self> {
|
||||
let int_to_appropriate_width = |v: &Value| {
|
||||
v.as_integer()
|
||||
.ok_or(GPSE::from_kind(GPSEK::HeaderTypeError)).and_then(i64_to_i8)
|
||||
};
|
||||
|
||||
match *v {
|
||||
Value::Table(ref map) => {
|
||||
Ok(GPSValue::new(
|
||||
map.get("degree")
|
||||
.ok_or_else(|| GPSE::from_kind(GPSEK::DegreeMissing))
|
||||
.and_then(|v| match *v {
|
||||
Value::Integer(i) => i64_to_i8(i),
|
||||
_ => Err(GPSE::from_kind(GPSEK::HeaderTypeError)),
|
||||
})?,
|
||||
.and_then(&int_to_appropriate_width)?,
|
||||
|
||||
map
|
||||
.get("minutes")
|
||||
.ok_or_else(|| GPSE::from_kind(GPSEK::MinutesMissing))
|
||||
.and_then(|v| match *v {
|
||||
Value::Integer(i) => i64_to_i8(i),
|
||||
_ => Err(GPSE::from_kind(GPSEK::HeaderTypeError)),
|
||||
})?,
|
||||
.and_then(&int_to_appropriate_width)?,
|
||||
|
||||
map
|
||||
.get("seconds")
|
||||
.ok_or_else(|| GPSE::from_kind(GPSEK::SecondsMissing))
|
||||
.and_then(|v| match *v {
|
||||
Value::Integer(i) => i64_to_i8(i),
|
||||
_ => Err(GPSE::from_kind(GPSEK::HeaderTypeError)),
|
||||
})?
|
||||
.and_then(&int_to_appropriate_width)?
|
||||
))
|
||||
}
|
||||
_ => Err(GPSE::from_kind(GPSEK::TypeError))
|
||||
|
@ -154,22 +150,18 @@ impl Into<Value> for Coordinates {
|
|||
|
||||
impl FromValue for Coordinates {
|
||||
fn from_value(v: &Value) -> Result<Self> {
|
||||
match *v {
|
||||
Value::Table(ref map) => {
|
||||
Ok(Coordinates::new(
|
||||
match map.get("longitude") {
|
||||
Some(v) => GPSValue::from_value(v),
|
||||
None => Err(GPSE::from_kind(GPSEK::LongitudeMissing)),
|
||||
}?,
|
||||
v.as_table()
|
||||
.ok_or(GPSE::from_kind(GPSEK::TypeError))
|
||||
.and_then(|t| {
|
||||
let get = |m: &BTreeMap<_, _>, what: &'static str, ek| -> Result<GPSValue> {
|
||||
m.get(what).ok_or(GPSE::from_kind(ek)).and_then(GPSValue::from_value)
|
||||
};
|
||||
|
||||
match map.get("latitude") {
|
||||
Some(v) => GPSValue::from_value(v),
|
||||
None => Err(GPSE::from_kind(GPSEK::LongitudeMissing)),
|
||||
}?
|
||||
Ok(Coordinates::new(
|
||||
get(t, "longitude", GPSEK::LongitudeMissing)?,
|
||||
get(t, "latitude", GPSEK::LatitudeMissing)?
|
||||
))
|
||||
}
|
||||
_ => Err(GPSE::from_kind(GPSEK::TypeError))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ error_chain! {
|
|||
|
||||
foreign_links {
|
||||
TomlQueryError(::toml_query::error::Error);
|
||||
UrlError(::url::ParseError);
|
||||
}
|
||||
|
||||
errors {
|
||||
|
|
|
@ -641,13 +641,9 @@ pub mod store_check {
|
|||
let mut map = HashMap::new();
|
||||
for element in iter {
|
||||
debug!("Checking element = {:?}", element);
|
||||
let entry = match element? {
|
||||
Some(e) => e,
|
||||
None => {
|
||||
let e = String::from("TODO: Not yet handled");
|
||||
return Err(e).map_err(From::from);
|
||||
},
|
||||
};
|
||||
let entry = element?.ok_or_else(|| {
|
||||
LE::from(String::from("TODO: Not yet handled"))
|
||||
})?;
|
||||
|
||||
debug!("Checking entry = {:?}", entry.get_location());
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ use std::collections::BTreeMap;
|
|||
|
||||
use toml::Value;
|
||||
|
||||
use error::RefError as RE;
|
||||
use error::RefErrorKind as REK;
|
||||
use error::Result;
|
||||
|
||||
|
@ -39,13 +40,10 @@ impl RefFlags {
|
|||
fn get_field(v: &Value, key: &str) -> Result<bool> {
|
||||
use toml_query::read::TomlValueReadExt;
|
||||
|
||||
v.read(key)
|
||||
.map_err(From::from)
|
||||
.and_then(|toml| match toml {
|
||||
Some(&Value::Boolean(b)) => Ok(b),
|
||||
Some(_) => Err(REK::HeaderTypeError.into()),
|
||||
None => Err(REK::HeaderFieldMissingError.into()),
|
||||
})
|
||||
v.read(key)?
|
||||
.ok_or(RE::from_kind(REK::HeaderFieldMissingError))?
|
||||
.as_bool()
|
||||
.ok_or(REK::HeaderTypeError.into())
|
||||
}
|
||||
|
||||
Ok(RefFlags {
|
||||
|
|
|
@ -142,20 +142,16 @@ fn check_changed<R: Ref>(r: &R) -> bool {
|
|||
}
|
||||
|
||||
fn check_changed_content<R: Ref>(r: &R) -> bool {
|
||||
let eq = r.get_current_hash()
|
||||
r.get_current_hash()
|
||||
.and_then(|hash| r.get_stored_hash().map(|stored| (hash, stored)))
|
||||
.map(|(hash, stored)| hash == stored);
|
||||
|
||||
match eq {
|
||||
Ok(eq) => eq,
|
||||
Err(e) => {
|
||||
.map(|(hash, stored)| hash == stored)
|
||||
.unwrap_or_else(|e| {
|
||||
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
|
||||
false
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn check_changed_permiss<R: Ref>(_: &R) -> bool {
|
||||
|
|
|
@ -140,17 +140,12 @@ impl Ref for Entry {
|
|||
/// 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<H: Hasher>(&self, h: &H) -> Result<String> {
|
||||
match self.get_header().read(&format!("ref.content_hash.{}", h.hash_name())[..])? {
|
||||
// content hash stored...
|
||||
Some(&Value::String(ref s)) => Ok(s.clone()),
|
||||
|
||||
// content hash header field has wrong type
|
||||
Some(_) => Err(RE::from_kind(REK::HeaderTypeError)),
|
||||
|
||||
// content hash not stored
|
||||
None => Err(RE::from_kind(REK::HeaderFieldMissingError)),
|
||||
|
||||
}
|
||||
self.get_header()
|
||||
.read(&format!("ref.content_hash.{}", h.hash_name())[..])?
|
||||
.ok_or(RE::from_kind(REK::HeaderFieldMissingError))?
|
||||
.as_str()
|
||||
.map(String::from)
|
||||
.ok_or(RE::from_kind(REK::HeaderTypeError))
|
||||
}
|
||||
|
||||
/// Get the hash of the link target by reading the link target and hashing the contents
|
||||
|
@ -207,11 +202,9 @@ impl Ref for Entry {
|
|||
.read("ref.permissions.ro")
|
||||
.chain_err(|| REK::HeaderFieldReadError)
|
||||
.and_then(|ro| {
|
||||
match ro {
|
||||
Some(&Value::Boolean(b)) => Ok(b),
|
||||
Some(_) => Err(RE::from_kind(REK::HeaderTypeError)),
|
||||
None => Err(RE::from_kind(REK::HeaderFieldMissingError)),
|
||||
}
|
||||
ro.ok_or(RE::from_kind(REK::HeaderFieldMissingError))?
|
||||
.as_bool()
|
||||
.ok_or(RE::from_kind(REK::HeaderTypeError))
|
||||
})
|
||||
.and_then(|ro| self.get_current_permissions().map(|perm| ro == perm.readonly()))
|
||||
.chain_err(|| REK::RefTargetCannotReadPermissions)
|
||||
|
@ -251,11 +244,12 @@ impl Ref for Entry {
|
|||
|
||||
/// Get the path of the file which is reffered to by this Ref
|
||||
fn fs_file(&self) -> Result<PathBuf> {
|
||||
match self.get_header().read("ref.path")? {
|
||||
Some(&Value::String(ref s)) => Ok(PathBuf::from(s)),
|
||||
Some(_) => Err(RE::from_kind(REK::HeaderTypeError)),
|
||||
None => Err(RE::from_kind(REK::HeaderFieldMissingError)),
|
||||
}
|
||||
self.get_header()
|
||||
.read("ref.path")?
|
||||
.ok_or(RE::from_kind(REK::HeaderFieldMissingError))?
|
||||
.as_str()
|
||||
.map(PathBuf::from)
|
||||
.ok_or(RE::from_kind(REK::HeaderTypeError))
|
||||
}
|
||||
|
||||
/// Re-find a referenced file
|
||||
|
|
|
@ -25,7 +25,6 @@ use error::Result;
|
|||
|
||||
use libimagstore::store::Entry;
|
||||
|
||||
use toml::Value;
|
||||
use toml_query::read::TomlValueReadExt;
|
||||
|
||||
/// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash
|
||||
|
@ -34,22 +33,22 @@ pub fn hash_path(pb: &PathBuf) -> Result<String> {
|
|||
use crypto::sha1::Sha1;
|
||||
use crypto::digest::Digest;
|
||||
|
||||
match pb.to_str() {
|
||||
Some(s) => {
|
||||
pb.to_str()
|
||||
.ok_or(RE::from_kind(REK::PathUTF8Error))
|
||||
.map(|s| {
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.input_str(s);
|
||||
Ok(hasher.result_str())
|
||||
},
|
||||
None => return Err(RE::from_kind(REK::PathUTF8Error)),
|
||||
}
|
||||
hasher.result_str()
|
||||
})
|
||||
}
|
||||
|
||||
/// Read the reference from a file
|
||||
pub fn read_reference(refentry: &Entry) -> Result<PathBuf> {
|
||||
match refentry.get_header().read("ref.path")? {
|
||||
Some(&Value::String(ref s)) => Ok(PathBuf::from(s)),
|
||||
Some(_) => Err(RE::from_kind(REK::HeaderTypeError)),
|
||||
None => Err(RE::from_kind(REK::HeaderFieldMissingError)),
|
||||
}
|
||||
refentry.get_header()
|
||||
.read("ref.path")?
|
||||
.ok_or(RE::from_kind(REK::HeaderFieldMissingError))?
|
||||
.as_str()
|
||||
.ok_or(RE::from_kind(REK::HeaderTypeError))
|
||||
.map(PathBuf::from)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,33 +49,39 @@ pub trait Tagable {
|
|||
impl Tagable for Value {
|
||||
|
||||
fn get_tags(&self) -> Result<Vec<Tag>> {
|
||||
let tags = self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError)?;
|
||||
|
||||
match tags {
|
||||
Some(&Value::Array(ref tags)) => {
|
||||
if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
|
||||
return Err(TagErrorKind::TagTypeError.into());
|
||||
}
|
||||
if tags.iter().any(|t| match *t {
|
||||
Value::String(ref s) => !is_tag_str(s).is_ok(),
|
||||
_ => unreachable!()})
|
||||
{
|
||||
return Err(TagErrorKind::NotATag.into());
|
||||
}
|
||||
|
||||
Ok(tags.iter()
|
||||
.cloned()
|
||||
.map(|t| {
|
||||
match t {
|
||||
Value::String(s) => s,
|
||||
_ => unreachable!(),
|
||||
self.read("tag.values")
|
||||
.chain_err(|| TagErrorKind::HeaderReadError)?
|
||||
.map(|val| {
|
||||
debug!("Got Value of tags...");
|
||||
val.as_array()
|
||||
.map(|tags| {
|
||||
debug!("Got Array<T> of tags...");
|
||||
if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
|
||||
debug!("Got Array<T>, T != String of tags: {:?}", tags);
|
||||
return Err(TagErrorKind::TagTypeError.into());
|
||||
}
|
||||
debug!("Got Array<String> of tags...");
|
||||
if tags.iter().any(|t| match *t {
|
||||
Value::String(ref s) => !is_tag_str(s).is_ok(),
|
||||
_ => unreachable!()})
|
||||
{
|
||||
debug!("At least one tag is not a valid tag string");
|
||||
return Err(TagErrorKind::NotATag.into());
|
||||
}
|
||||
|
||||
Ok(tags.iter()
|
||||
.cloned()
|
||||
.map(|t| {
|
||||
match t {
|
||||
Value::String(s) => s,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
})
|
||||
.collect())
|
||||
},
|
||||
None => Ok(vec![]),
|
||||
_ => Err(TagErrorKind::TagTypeError.into()),
|
||||
}
|
||||
.unwrap_or(Ok(vec![]))
|
||||
})
|
||||
.unwrap_or(Ok(vec![]))
|
||||
}
|
||||
|
||||
fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
|
||||
|
|
|
@ -42,40 +42,36 @@ impl Readline {
|
|||
let histignspace = c.lookup("ui.cli.readline_history_ignore_space").ok_or(IEK::ConfigError)?;
|
||||
let prompt = c.lookup("ui.cli.readline_prompt").ok_or(IEK::ConfigError)?;
|
||||
|
||||
let histfile = match histfile {
|
||||
Value::String(s) => PathBuf::from(s),
|
||||
_ => Err(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)
|
||||
}?;
|
||||
let histfile = histfile
|
||||
.as_str()
|
||||
.map(PathBuf::from)
|
||||
.ok_or(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)?;
|
||||
|
||||
let histsize = match histsize {
|
||||
Value::Integer(i) => i,
|
||||
_ => Err(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)
|
||||
}?;
|
||||
let histsize = histsize
|
||||
.as_int()
|
||||
.ok_or(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)?;
|
||||
|
||||
let histigndups = match histigndups {
|
||||
Value::Boolean(b) => b,
|
||||
_ => Err(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)
|
||||
}?;
|
||||
let histigndups = histigndups
|
||||
.as_bool()
|
||||
.ok_or(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)?;
|
||||
|
||||
let histignspace = match histignspace {
|
||||
Value::Boolean(b) => b,
|
||||
_ => Err(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)
|
||||
}?;
|
||||
let histignspace = histignspace
|
||||
.as_bool()
|
||||
.ok_or(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)?;
|
||||
|
||||
let prompt = match prompt {
|
||||
Value::String(s) => s,
|
||||
_ => Err(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)
|
||||
}?;
|
||||
let prompt = prompt
|
||||
.as_str()
|
||||
.ok_or(IE::from_kind(IEK::ConfigTypeError))
|
||||
.chain_err(|| IEK::ConfigError)
|
||||
.chain_err(|| IEK::ReadlineError)?;
|
||||
|
||||
let config = Config::builder().
|
||||
.max_history_size(histsize)
|
||||
|
|
|
@ -70,14 +70,13 @@ pub fn get_id(matches: &ArgMatches) -> Result<Vec<StoreId>> {
|
|||
pub fn get_or_select_id(matches: &ArgMatches, store_path: &PathBuf) -> Result<Vec<StoreId>> {
|
||||
use interactor::{pick_file, default_menu_cmd};
|
||||
|
||||
match get_id(matches).chain_err(|| IEK::IdSelectingError) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => {
|
||||
get_id(matches)
|
||||
.chain_err(|| IEK::IdSelectingError)
|
||||
.or_else(|_| {
|
||||
let path = store_path.clone();
|
||||
let p = pick_file(default_menu_cmd, path).chain_err(|| IEK::IdSelectingError)?;
|
||||
let id = StoreId::new_baseless(p).chain_err(|| IEK::StoreIdParsingError)?;
|
||||
let p = pick_file(default_menu_cmd, path).chain_err(|| IEK::IdSelectingError)?;
|
||||
let id = StoreId::new_baseless(p).chain_err(|| IEK::StoreIdParsingError)?;
|
||||
Ok(vec![id])
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue