diff --git a/imag-store/Cargo.toml b/imag-store/Cargo.toml index 66648cb1..13e8ebae 100644 --- a/imag-store/Cargo.toml +++ b/imag-store/Cargo.toml @@ -34,3 +34,6 @@ path = "../libimagutil" [dependencies.libimagerror] path = "../libimagerror" +[features] +early-panic = [ "libimagstore/early-panic" ] + diff --git a/imag-store/src/create.rs b/imag-store/src/create.rs index cfdbd01b..e015595f 100644 --- a/imag-store/src/create.rs +++ b/imag-store/src/create.rs @@ -28,10 +28,10 @@ use std::io::stderr; use std::process::exit; use clap::ArgMatches; +use toml::Value; use libimagrt::runtime::Runtime; use libimagstore::store::Entry; -use libimagstore::store::EntryHeader; use libimagstore::storeid::StoreId; use libimagerror::trace::trace_error_exit; use libimagutil::debug_result::*; @@ -70,10 +70,11 @@ pub fn create(rt: &Runtime) { .or_else(|_| create_with_content_and_header(rt, &path, String::new(), - EntryHeader::new())) + Entry::default_header())) } else { debug!("Creating entry"); - create_with_content_and_header(rt, &path, String::new(), EntryHeader::new()) + create_with_content_and_header(rt, &path, String::new(), + Entry::default_header()) } .unwrap_or_else(|e| { error!("Error building Entry"); @@ -100,8 +101,8 @@ fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> R debug!("Got content with len = {}", content.len()); let header = matches.subcommand_matches("entry") - .map_or_else(EntryHeader::new, - |entry_matches| build_toml_header(entry_matches, EntryHeader::new())); + .map_or_else(|| Entry::default_header(), + |entry_matches| build_toml_header(entry_matches, Entry::default_header())); create_with_content_and_header(rt, path, content, header) } @@ -138,7 +139,7 @@ fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> Res fn create_with_content_and_header(rt: &Runtime, path: &StoreId, content: String, - header: EntryHeader) -> Result<()> + header: Value) -> Result<()> { debug!("Creating entry with content at {:?}", path); rt.store() diff --git a/imag-store/src/retrieve.rs b/imag-store/src/retrieve.rs index 4d17e817..19c85b16 100644 --- a/imag-store/src/retrieve.rs +++ b/imag-store/src/retrieve.rs @@ -20,7 +20,6 @@ use std::path::PathBuf; use clap::ArgMatches; -use toml::Value; use libimagstore::store::FileLockEntry; use libimagstore::storeid::StoreId; @@ -70,8 +69,7 @@ pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) { unimplemented!() } else { debug!("Printing header as TOML..."); - // We have to Value::Table() for Display - println!("{}", Value::Table(e.get_header().clone().into())) + println!("{}", e.get_header()) } } diff --git a/imag-store/src/util.rs b/imag-store/src/util.rs index 8e7002c9..5da04666 100644 --- a/imag-store/src/util.rs +++ b/imag-store/src/util.rs @@ -24,13 +24,11 @@ use std::str::Split; use clap::ArgMatches; use toml::Value; -use libimagstore::store::EntryHeader; use libimagutil::key_value_split::IntoKeyValue; -pub fn build_toml_header(matches: &ArgMatches, header: EntryHeader) -> EntryHeader { +pub fn build_toml_header(matches: &ArgMatches, mut header: Value) -> Value { debug!("Building header from cli spec"); if let Some(headerspecs) = matches.values_of("header") { - let mut main = header.into(); let kvs = headerspecs.into_iter() .filter_map(|hs| { debug!("- Processing: '{}'", hs); @@ -42,18 +40,16 @@ pub fn build_toml_header(matches: &ArgMatches, header: EntryHeader) -> EntryHead let (key, value) = tpl.into(); debug!("Splitting: {:?}", key); let mut split = key.split('.'); - let current = split.next(); - if current.is_some() { - insert_key_into(String::from(current.unwrap()), &mut split, Cow::Owned(value), &mut main); + match (split.next(), &mut header) { + (Some(cur), &mut Value::Table(ref mut hdr)) => + insert_key_into(String::from(cur), &mut split, Cow::Owned(value), hdr), + _ => { } } } - - debug!("Header = {:?}", main); - EntryHeader::from(main) - } else { - debug!("Header = {:?}", header); - header } + + debug!("Header = {:?}", header); + header } fn insert_key_into<'a>(current: String, diff --git a/libimagentryfilter/src/builtin/header/field_exists.rs b/libimagentryfilter/src/builtin/header/field_exists.rs index 9999b3df..5fe672da 100644 --- a/libimagentryfilter/src/builtin/header/field_exists.rs +++ b/libimagentryfilter/src/builtin/header/field_exists.rs @@ -18,6 +18,7 @@ // use libimagstore::store::Entry; +use libimagstore::toml_ext::TomlValueExt; use builtin::header::field_path::FieldPath; use filters::filter::Filter; diff --git a/libimagentryfilter/src/builtin/header/field_isempty.rs b/libimagentryfilter/src/builtin/header/field_isempty.rs index d243d78f..7a9c6521 100644 --- a/libimagentryfilter/src/builtin/header/field_isempty.rs +++ b/libimagentryfilter/src/builtin/header/field_isempty.rs @@ -18,6 +18,7 @@ // use libimagstore::store::Entry; +use libimagstore::toml_ext::TomlValueExt; use builtin::header::field_path::FieldPath; use filters::filter::Filter; diff --git a/libimagentryfilter/src/builtin/header/field_predicate.rs b/libimagentryfilter/src/builtin/header/field_predicate.rs index 361ed0fe..45aa332c 100644 --- a/libimagentryfilter/src/builtin/header/field_predicate.rs +++ b/libimagentryfilter/src/builtin/header/field_predicate.rs @@ -18,6 +18,7 @@ // use libimagstore::store::Entry; +use libimagstore::toml_ext::TomlValueExt; use builtin::header::field_path::FieldPath; use filters::filter::Filter; diff --git a/libimagentryfilter/src/builtin/header/version/eq.rs b/libimagentryfilter/src/builtin/header/version/eq.rs index ac805a68..1ca38f8b 100644 --- a/libimagentryfilter/src/builtin/header/version/eq.rs +++ b/libimagentryfilter/src/builtin/header/version/eq.rs @@ -21,6 +21,7 @@ use semver::Version; use toml::Value; use libimagstore::store::Entry; +use libimagstore::toml_ext::TomlValueExt; use filters::filter::Filter; diff --git a/libimagentryfilter/src/builtin/header/version/gt.rs b/libimagentryfilter/src/builtin/header/version/gt.rs index daded755..8e3873fb 100644 --- a/libimagentryfilter/src/builtin/header/version/gt.rs +++ b/libimagentryfilter/src/builtin/header/version/gt.rs @@ -21,6 +21,7 @@ use semver::Version; use toml::Value; use libimagstore::store::Entry; +use libimagstore::toml_ext::TomlValueExt; use filters::filter::Filter; diff --git a/libimagentryfilter/src/builtin/header/version/lt.rs b/libimagentryfilter/src/builtin/header/version/lt.rs index 02c35cd3..c475b436 100644 --- a/libimagentryfilter/src/builtin/header/version/lt.rs +++ b/libimagentryfilter/src/builtin/header/version/lt.rs @@ -21,6 +21,7 @@ use semver::Version; use toml::Value; use libimagstore::store::Entry; +use libimagstore::toml_ext::TomlValueExt; use filters::filter::Filter; diff --git a/libimagentrylink/src/external.rs b/libimagentrylink/src/external.rs index 4959428b..0d6aab08 100644 --- a/libimagentrylink/src/external.rs +++ b/libimagentrylink/src/external.rs @@ -38,6 +38,7 @@ use libimagstore::store::FileLockEntry; use libimagstore::store::Store; use libimagstore::storeid::StoreId; use libimagstore::storeid::IntoStoreId; +use libimagstore::toml_ext::TomlValueExt; use libimagutil::debug_result::*; use error::LinkError as LE; diff --git a/libimagentrylink/src/internal.rs b/libimagentrylink/src/internal.rs index 48f5d819..83add039 100644 --- a/libimagentrylink/src/internal.rs +++ b/libimagentrylink/src/internal.rs @@ -19,8 +19,8 @@ use libimagstore::storeid::StoreId; use libimagstore::store::Entry; -use libimagstore::store::EntryHeader; use libimagstore::store::Result as StoreResult; +use libimagstore::toml_ext::TomlValueExt; use libimagerror::into::IntoError; use error::LinkErrorKind as LEK; @@ -337,7 +337,7 @@ impl InternalLinker for Entry { } -fn rewrite_links>(header: &mut EntryHeader, links: I) -> Result<()> { +fn rewrite_links>(header: &mut Value, links: I) -> Result<()> { let links = try!(links.into_values() .fold(Ok(vec![]), |acc, elem| { acc.and_then(move |mut v| { diff --git a/libimagentrytag/src/tagable.rs b/libimagentrytag/src/tagable.rs index bff77932..9e0fa2fd 100644 --- a/libimagentrytag/src/tagable.rs +++ b/libimagentrytag/src/tagable.rs @@ -19,8 +19,9 @@ use itertools::Itertools; -use libimagstore::store::{Entry, EntryHeader}; +use libimagstore::store::Entry; use libimagerror::into::IntoError; +use libimagstore::toml_ext::TomlValueExt; use error::TagErrorKind; use error::MapErrInto; @@ -43,7 +44,7 @@ pub trait Tagable { } -impl Tagable for EntryHeader { +impl Tagable for Value { fn get_tags(&self) -> Result> { let tags = try!(self.read("imag.tags").map_err_into(TagErrorKind::HeaderReadError)); diff --git a/libimagentryview/src/builtin/plain.rs b/libimagentryview/src/builtin/plain.rs index d96f4604..f40b2527 100644 --- a/libimagentryview/src/builtin/plain.rs +++ b/libimagentryview/src/builtin/plain.rs @@ -40,7 +40,7 @@ impl Viewer for PlainViewer { fn view_entry(&self, e: &Entry) -> Result<()> { if self.show_header { - println!("{}", e.get_header().header()); + println!("{}", e.get_header()); } println!("{}", e.get_content()); Ok(()) diff --git a/libimagentryview/src/builtin/stdout.rs b/libimagentryview/src/builtin/stdout.rs index 703281fd..4d431f2e 100644 --- a/libimagentryview/src/builtin/stdout.rs +++ b/libimagentryview/src/builtin/stdout.rs @@ -44,7 +44,7 @@ impl Viewer for StdoutViewer { fn view_entry(&self, e: &Entry) -> Result<()> { if self.view_header { - println!("{}", encode_str(e.get_header().header())); + println!("{}", encode_str(e.get_header())); } if self.view_content { diff --git a/libimagnotes/src/note.rs b/libimagnotes/src/note.rs index 2397948c..2edf1d60 100644 --- a/libimagnotes/src/note.rs +++ b/libimagnotes/src/note.rs @@ -30,6 +30,7 @@ use libimagstore::storeid::StoreId; use libimagstore::storeid::StoreIdIterator; use libimagstore::store::FileLockEntry; use libimagstore::store::Store; +use libimagstore::toml_ext::TomlValueExt; use libimagentrytag::tag::{Tag, TagSlice}; use libimagentrytag::tagable::Tagable; use libimagentrytag::result::Result as TagResult; diff --git a/libimagref/src/reference.rs b/libimagref/src/reference.rs index 0b39285a..1b307ba7 100644 --- a/libimagref/src/reference.rs +++ b/libimagref/src/reference.rs @@ -33,6 +33,7 @@ use libimagstore::store::FileLockEntry; use libimagstore::storeid::StoreId; use libimagstore::storeid::IntoStoreId; use libimagstore::store::Store; +use libimagstore::toml_ext::TomlValueExt; use libimagerror::into::IntoError; use toml::Value; diff --git a/libimagruby/src/entry.rs b/libimagruby/src/entry.rs index 7eb6b672..0ac88c07 100644 --- a/libimagruby/src/entry.rs +++ b/libimagruby/src/entry.rs @@ -21,11 +21,12 @@ use std::collections::BTreeMap; use std::error::Error; use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject}; +use toml::Value; -use libimagstore::store::EntryHeader; use libimagstore::store::EntryContent; use libimagstore::store::Entry; use libimagstore::storeid::StoreId; +use libimagstore::toml_ext::TomlValueExt; use ruby_utils::IntoToml; use toml_utils::IntoRuby; @@ -130,7 +131,7 @@ methods!( use toml::Value; let entryheader = match typecheck!(hdr or return NilClass::new()).into_toml() { - Value::Table(t) => EntryHeader::from(t), + Value::Table(t) => Value::Table(t), _ => { let ec = Class::from_existing("RuntimeError"); VM::raise(ec, "Something weird happened. Hash seems to be not a Hash"); @@ -175,10 +176,10 @@ methods!( ); -wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER); +wrappable_struct!(Value, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER); class!(REntryHeader); -impl_wrap!(EntryHeader => ENTRY_HEADER_WRAPPER); -impl_unwrap!(REntryHeader => EntryHeader => ENTRY_HEADER_WRAPPER); +impl_wrap!(Value => ENTRY_HEADER_WRAPPER); +impl_unwrap!(REntryHeader => Value => ENTRY_HEADER_WRAPPER); impl_verified_object!(REntryHeader); methods!( @@ -186,7 +187,7 @@ methods!( itself, fn r_entry_header_new() -> AnyObject { - EntryHeader::new().wrap() + Entry::default_header().wrap() } fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean { diff --git a/libimagstore/Cargo.toml b/libimagstore/Cargo.toml index 656092f5..5e198995 100644 --- a/libimagstore/Cargo.toml +++ b/libimagstore/Cargo.toml @@ -39,3 +39,28 @@ env_logger = "0.3" default = [] verify = [] +# Enable panic!()s if critical errors occur. +# +# # Howto +# +# To enable this, put +# +# ```toml +# [features] +# early-panic = [ "libimagstore/early-panic" ] +# ``` +# +# In the crate depending on this library and compile your crate with +# `cargo build --features early-panic`. This way, the `libimagstore` +# implementation fails via `panic!()` instead of propagating errors which have +# to be printed somewhere to be visible. +# +# # WARNING +# +# The behaviour of the store implementation might be broken with this, resulting +# in partially written store entries and/or worse, so this is +# +# _NOT INTENDED FOR PRODUCTION USE_! +# +early-panic=[] + diff --git a/libimagstore/src/lib.rs b/libimagstore/src/lib.rs index a696f7cc..56c3a742 100644 --- a/libimagstore/src/lib.rs +++ b/libimagstore/src/lib.rs @@ -46,11 +46,13 @@ extern crate walkdir; #[macro_use] extern crate libimagerror; #[macro_use] extern crate libimagutil; +#[macro_use] mod util; + pub mod storeid; pub mod error; pub mod hook; pub mod store; mod configuration; mod file_abstraction; -mod toml_ext; +pub mod toml_ext; diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index de9988d9..d245feb3 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -23,7 +23,6 @@ use std::path::PathBuf; use std::result::Result as RResult; use std::sync::Arc; use std::sync::RwLock; -use std::collections::BTreeMap; use std::io::Read; use std::convert::From; use std::convert::Into; @@ -34,13 +33,12 @@ use std::fmt::Formatter; use std::fmt::Debug; use std::fmt::Error as FMTError; -use toml::{Table, Value}; +use toml::Value; use regex::Regex; use glob::glob; use walkdir::WalkDir; use walkdir::Iter as WalkDirIter; -use error::{ParserErrorKind, ParserError}; use error::{StoreError as SE, StoreErrorKind as SEK}; use error::MapErrInto; use storeid::{IntoStoreId, StoreId, StoreIdIterator}; @@ -944,7 +942,15 @@ impl<'a> DerefMut for FileLockEntry<'a> { impl<'a> Drop for FileLockEntry<'a> { /// This will silently ignore errors, use `Store::update` if you want to catch the errors fn drop(&mut self) { - let _ = self.store._update(self, true); + use libimagerror::trace::trace_error_dbg; + + match self.store._update(self, true) { + Err(e) => { + trace_error_dbg(&e); + if_cfg_panic!("ERROR WHILE DROPPING: {:?}", e); + }, + Ok(_) => { }, + } } } @@ -960,176 +966,6 @@ impl<'a> Drop for FileLockEntry<'a> { /// `EntryContent` type pub type EntryContent = String; -/// `EntryHeader` -/// -/// This is basically a wrapper around `toml::Table` which provides convenience to the user of the -/// library. -#[derive(Debug, Clone, PartialEq)] -pub struct EntryHeader(Value); - -pub type EntryResult = RResult; - -/** - * Wrapper type around file header (TOML) object - */ -impl EntryHeader { - - pub fn new() -> EntryHeader { - EntryHeader(build_default_header()) - } - - pub fn header(&self) -> &Value { - &self.0 - } - - fn from_table(t: Table) -> EntryHeader { - EntryHeader(Value::Table(t)) - } - - pub fn parse(s: &str) -> EntryResult { - use toml::Parser; - - let mut parser = Parser::new(s); - parser.parse() - .ok_or(ParserErrorKind::TOMLParserErrors.into()) - .and_then(verify_header_consistency) - .map(EntryHeader::from_table) - } - - pub fn verify(&self) -> Result<()> { - match self.0 { - Value::Table(ref t) => verify_header(&t), - _ => Err(SE::new(SEK::HeaderTypeFailure, None)), - } - } - - #[inline] - pub fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result { - self.0.insert_with_sep(spec, sep, v) - } - - #[inline] - pub fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result> { - self.0.set_with_sep(spec, sep, v) - } - - #[inline] - pub fn read_with_sep(&self, spec: &str, splitchr: char) -> Result> { - self.0.read_with_sep(spec, splitchr) - } - - #[inline] - pub fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result> { - self.0.delete_with_sep(spec, splitchr) - } - - #[inline] - pub fn insert(&mut self, spec: &str, v: Value) -> Result { - self.0.insert(spec, v) - } - - #[inline] - pub fn set(&mut self, spec: &str, v: Value) -> Result> { - self.0.set(spec, v) - } - - #[inline] - pub fn read(&self, spec: &str) -> Result> { - self.0.read(spec) - } - - #[inline] - pub fn delete(&mut self, spec: &str) -> Result> { - self.0.delete(spec) - } - -} - -impl Into for EntryHeader { - - fn into(self) -> Table { - match self.0 { - Value::Table(t) => t, - _ => panic!("EntryHeader is not a table!"), - } - } - -} - -impl From
for EntryHeader { - - fn from(t: Table) -> EntryHeader { - EntryHeader(Value::Table(t)) - } - -} - -fn build_default_header() -> Value { // BTreeMap - let mut m = BTreeMap::new(); - - m.insert(String::from("imag"), { - let mut imag_map = BTreeMap::::new(); - - imag_map.insert(String::from("version"), Value::String(String::from(version!()))); - imag_map.insert(String::from("links"), Value::Array(vec![])); - - Value::Table(imag_map) - }); - - Value::Table(m) -} -fn verify_header(t: &Table) -> Result<()> { - if !has_main_section(t) { - Err(SE::from(ParserErrorKind::MissingMainSection.into_error())) - } else if !has_imag_version_in_main_section(t) { - Err(SE::from(ParserErrorKind::MissingVersionInfo.into_error())) - } else if !has_only_tables(t) { - debug!("Could not verify that it only has tables in its base table"); - Err(SE::from(ParserErrorKind::NonTableInBaseTable.into_error())) - } else { - Ok(()) - } -} - -fn verify_header_consistency(t: Table) -> EntryResult
{ - verify_header(&t) - .map_err(Box::new) - .map_err(|e| ParserErrorKind::HeaderInconsistency.into_error_with_cause(e)) - .map(|_| t) -} - -fn has_only_tables(t: &Table) -> bool { - debug!("Verifying that table has only tables"); - t.iter().all(|(_, x)| if let Value::Table(_) = *x { true } else { false }) -} - -fn has_main_section(t: &Table) -> bool { - t.contains_key("imag") && - match t.get("imag") { - Some(&Value::Table(_)) => true, - Some(_) => false, - None => false, - } -} - -fn has_imag_version_in_main_section(t: &Table) -> bool { - use semver::Version; - - match *t.get("imag").unwrap() { - Value::Table(ref sec) => { - sec.get("version") - .and_then(|v| { - match *v { - Value::String(ref s) => Some(Version::parse(&s[..]).is_ok()), - _ => Some(false), - } - }) - .unwrap_or(false) - } - _ => false, - } -} - /** * An Entry of the store * @@ -1138,7 +974,7 @@ fn has_imag_version_in_main_section(t: &Table) -> bool { #[derive(Debug, Clone)] pub struct Entry { location: StoreId, - header: EntryHeader, + header: Value, content: EntryContent, } @@ -1147,11 +983,15 @@ impl Entry { pub fn new(loc: StoreId) -> Entry { Entry { location: loc, - header: EntryHeader::new(), + header: Entry::default_header(), content: EntryContent::new() } } + pub fn default_header() -> Value { // BTreeMap + Value::default_header() + } + pub fn from_reader(loc: S, file: &mut Read) -> Result { let text = { let mut s = String::new(); @@ -1187,14 +1027,14 @@ impl Entry { debug!("Header and content found. Yay! Building Entry object now"); Ok(Entry { location: try!(loc.into_storeid()), - header: try!(EntryHeader::parse(header.as_str())), + header: try!(Value::parse(header.as_str())), content: String::from(content), }) } pub fn to_str(&self) -> String { format!("---\n{header}---\n{content}", - header = ::toml::encode_str(&self.header.0), + header = ::toml::encode_str(&self.header), content = self.content) } @@ -1202,11 +1042,11 @@ impl Entry { &self.location } - pub fn get_header(&self) -> &EntryHeader { + pub fn get_header(&self) -> &Value { &self.header } - pub fn get_header_mut(&mut self) -> &mut EntryHeader { + pub fn get_header_mut(&mut self) -> &mut Value { &mut self.header } @@ -1314,7 +1154,7 @@ mod test { #[test] fn test_imag_section() { - use super::has_main_section; + use toml_ext::has_main_section; let mut map = BTreeMap::new(); map.insert("imag".into(), Value::Table(BTreeMap::new())); @@ -1324,7 +1164,7 @@ mod test { #[test] fn test_imag_invalid_section_type() { - use super::has_main_section; + use toml_ext::has_main_section; let mut map = BTreeMap::new(); map.insert("imag".into(), Value::Boolean(false)); @@ -1334,7 +1174,7 @@ mod test { #[test] fn test_imag_abscent_main_section() { - use super::has_main_section; + use toml_ext::has_main_section; let mut map = BTreeMap::new(); map.insert("not_imag".into(), Value::Boolean(false)); @@ -1344,7 +1184,7 @@ mod test { #[test] fn test_main_section_without_version() { - use super::has_imag_version_in_main_section; + use toml_ext::has_imag_version_in_main_section; let mut map = BTreeMap::new(); map.insert("imag".into(), Value::Table(BTreeMap::new())); @@ -1354,7 +1194,7 @@ mod test { #[test] fn test_main_section_with_version() { - use super::has_imag_version_in_main_section; + use toml_ext::has_imag_version_in_main_section; let mut map = BTreeMap::new(); let mut sub = BTreeMap::new(); @@ -1366,7 +1206,7 @@ mod test { #[test] fn test_main_section_with_version_in_wrong_type() { - use super::has_imag_version_in_main_section; + use toml_ext::has_imag_version_in_main_section; let mut map = BTreeMap::new(); let mut sub = BTreeMap::new(); @@ -1378,7 +1218,7 @@ mod test { #[test] fn test_verification_good() { - use super::verify_header_consistency; + use toml_ext::verify_header_consistency; let mut header = BTreeMap::new(); let sub = { @@ -1395,7 +1235,7 @@ mod test { #[test] fn test_verification_invalid_versionstring() { - use super::verify_header_consistency; + use toml_ext::verify_header_consistency; let mut header = BTreeMap::new(); let sub = { @@ -1413,7 +1253,7 @@ mod test { #[test] fn test_verification_current_version() { - use super::verify_header_consistency; + use toml_ext::verify_header_consistency; let mut header = BTreeMap::new(); let sub = { diff --git a/libimagstore/src/toml_ext.rs b/libimagstore/src/toml_ext.rs index c0bd79b2..1ac4cf11 100644 --- a/libimagstore/src/toml_ext.rs +++ b/libimagstore/src/toml_ext.rs @@ -17,10 +17,15 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use toml::Value; +use std::result::Result as RResult; +use std::collections::BTreeMap; + +use toml::{Table, Value}; use store::Result; +use error::StoreError as SE; use error::StoreErrorKind as SEK; +use error::{ParserErrorKind, ParserError}; use libimagerror::into::IntoError; pub trait TomlValueExt { @@ -353,10 +358,104 @@ impl Extract for Value { } } +pub type EntryResult = RResult; + +/// Extension trait for top-level toml::Value::Table, will only yield correct results on the +/// top-level Value::Table, but not on intermediate tables. +pub trait Header { + fn verify(&self) -> Result<()>; + fn parse(s: &str) -> EntryResult; + fn default_header() -> Value; +} + +impl Header for Value { + + fn verify(&self) -> Result<()> { + match *self { + Value::Table(ref t) => verify_header(&t), + _ => Err(SE::new(SEK::HeaderTypeFailure, None)), + } + } + + fn parse(s: &str) -> EntryResult { + use toml::Parser; + + let mut parser = Parser::new(s); + parser.parse() + .ok_or(ParserErrorKind::TOMLParserErrors.into()) + .and_then(verify_header_consistency) + .map(Value::Table) + } + + fn default_header() -> Value { + let mut m = BTreeMap::new(); + + m.insert(String::from("imag"), { + let mut imag_map = BTreeMap::::new(); + + imag_map.insert(String::from("version"), Value::String(String::from(version!()))); + imag_map.insert(String::from("links"), Value::Array(vec![])); + + Value::Table(imag_map) + }); + + Value::Table(m) + } + +} + +pub fn verify_header_consistency(t: Table) -> EntryResult
{ + verify_header(&t) + .map_err(Box::new) + .map_err(|e| ParserErrorKind::HeaderInconsistency.into_error_with_cause(e)) + .map(|_| t) +} + +fn verify_header(t: &Table) -> Result<()> { + if !has_main_section(t) { + Err(SE::from(ParserErrorKind::MissingMainSection.into_error())) + } else if !has_imag_version_in_main_section(t) { + Err(SE::from(ParserErrorKind::MissingVersionInfo.into_error())) + } else if !has_only_tables(t) { + debug!("Could not verify that it only has tables in its base table"); + Err(SE::from(ParserErrorKind::NonTableInBaseTable.into_error())) + } else { + Ok(()) + } +} + +fn has_only_tables(t: &Table) -> bool { + debug!("Verifying that table has only tables"); + t.iter().all(|(_, x)| is_match!(*x, Value::Table(_))) +} + +pub fn has_main_section(t: &Table) -> bool { + t.contains_key("imag") && is_match!(t.get("imag"), Some(&Value::Table(_))) +} + +pub fn has_imag_version_in_main_section(t: &Table) -> bool { + use semver::Version; + + match *t.get("imag").unwrap() { + Value::Table(ref sec) => { + sec.get("version") + .and_then(|v| { + match *v { + Value::String(ref s) => Some(Version::parse(&s[..]).is_ok()), + _ => Some(false), + } + }) + .unwrap_or(false) + } + _ => false, + } +} + + #[cfg(test)] mod test { extern crate env_logger; - use store::EntryHeader; + use super::TomlValueExt; use super::{tokenize, walk_header}; use super::Token; @@ -619,11 +718,7 @@ mod test { #[test] fn test_header_read() { - let v = create_header(); - let h = match v { - Value::Table(t) => EntryHeader::from(t), - _ => panic!("create_header() doesn't return a table!"), - }; + let h = create_header(); assert!(if let Ok(Some(Value::Table(_))) = h.read("a") { true } else { false }); assert!(if let Ok(Some(Value::Array(_))) = h.read("a.array") { true } else { false }); @@ -656,11 +751,7 @@ mod test { #[test] fn test_header_set_override() { let _ = env_logger::init(); - let v = create_header(); - let mut h = match v { - Value::Table(t) => EntryHeader::from(t), - _ => panic!("create_header() doesn't return a table!"), - }; + let mut h = create_header(); println!("Testing index 0"); assert_eq!(h.read("a.array.0").unwrap().unwrap(), Value::Integer(0)); @@ -686,11 +777,7 @@ mod test { #[test] fn test_header_set_new() { let _ = env_logger::init(); - let v = create_header(); - let mut h = match v { - Value::Table(t) => EntryHeader::from(t), - _ => panic!("create_header() doesn't return a table!"), - }; + let mut h = create_header(); assert!(h.read("a.foo").is_ok()); assert!(h.read("a.foo").unwrap().is_none()); @@ -727,11 +814,7 @@ mod test { #[test] fn test_header_insert_override() { let _ = env_logger::init(); - let v = create_header(); - let mut h = match v { - Value::Table(t) => EntryHeader::from(t), - _ => panic!("create_header() doesn't return a table!"), - }; + let mut h = create_header(); println!("Testing index 0"); assert_eq!(h.read("a.array.0").unwrap().unwrap(), Value::Integer(0)); @@ -756,11 +839,7 @@ mod test { #[test] fn test_header_insert_new() { let _ = env_logger::init(); - let v = create_header(); - let mut h = match v { - Value::Table(t) => EntryHeader::from(t), - _ => panic!("create_header() doesn't return a table!"), - }; + let mut h = create_header(); assert!(h.read("a.foo").is_ok()); assert!(h.read("a.foo").unwrap().is_none()); @@ -796,11 +875,7 @@ mod test { #[test] fn test_header_delete() { let _ = env_logger::init(); - let v = create_header(); - let mut h = match v { - Value::Table(t) => EntryHeader::from(t), - _ => panic!("create_header() doesn't return a table!"), - }; + let mut h = create_header(); assert!(if let Ok(Some(Value::Table(_))) = h.read("a") { true } else { false }); assert!(if let Ok(Some(Value::Array(_))) = h.read("a.array") { true } else { false }); @@ -815,3 +890,4 @@ mod test { } } + diff --git a/libimagstore/src/util.rs b/libimagstore/src/util.rs new file mode 100644 index 00000000..9ff4a145 --- /dev/null +++ b/libimagstore/src/util.rs @@ -0,0 +1,35 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +#[cfg(feature = "early-panic")] +#[macro_export] +macro_rules! if_cfg_panic { + () => { panic!() }; + ($msg:expr) => { panic!($msg) }; + ($fmt:expr, $($arg:tt)+) => { panic!($fmt, $($($arg),*)) }; +} + +#[cfg(not(feature = "early-panic"))] +#[macro_export] +macro_rules! if_cfg_panic { + () => { }; + ($msg:expr) => { }; + ($fmt:expr, $($arg:tt)+) => { }; +} +