Eliminate EntryHeader type
Therefor introduce the "Header" trait which is implemented on Value for backwards compatibility. Make the default_header() function publicly available as Entry::default_header()
This commit is contained in:
parent
2ca89b7329
commit
f1c1a1f9be
10 changed files with 135 additions and 215 deletions
|
@ -21,6 +21,7 @@ use semver::Version;
|
|||
use toml::Value;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
use libimagstore::toml_ext::TomlValueExt;
|
||||
|
||||
use filters::filter::Filter;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ use semver::Version;
|
|||
use toml::Value;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
use libimagstore::toml_ext::TomlValueExt;
|
||||
|
||||
use filters::filter::Filter;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ use semver::Version;
|
|||
use toml::Value;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
use libimagstore::toml_ext::TomlValueExt;
|
||||
|
||||
use filters::filter::Filter;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -21,6 +21,7 @@ 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -52,5 +52,5 @@ pub mod hook;
|
|||
pub mod store;
|
||||
mod configuration;
|
||||
mod file_abstraction;
|
||||
mod toml_ext;
|
||||
pub mod toml_ext;
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
@ -960,176 +958,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<V> = RResult<V, ParserError>;
|
||||
|
||||
/**
|
||||
* 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<EntryHeader> {
|
||||
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<bool> {
|
||||
self.0.insert_with_sep(spec, sep, v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>> {
|
||||
self.0.set_with_sep(spec, sep, v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>> {
|
||||
self.0.read_with_sep(spec, splitchr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>> {
|
||||
self.0.delete_with_sep(spec, splitchr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn insert(&mut self, spec: &str, v: Value) -> Result<bool> {
|
||||
self.0.insert(spec, v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set(&mut self, spec: &str, v: Value) -> Result<Option<Value>> {
|
||||
self.0.set(spec, v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read(&self, spec: &str) -> Result<Option<Value>> {
|
||||
self.0.read(spec)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn delete(&mut self, spec: &str) -> Result<Option<Value>> {
|
||||
self.0.delete(spec)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Into<Table> for EntryHeader {
|
||||
|
||||
fn into(self) -> Table {
|
||||
match self.0 {
|
||||
Value::Table(t) => t,
|
||||
_ => panic!("EntryHeader is not a table!"),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl From<Table> for EntryHeader {
|
||||
|
||||
fn from(t: Table) -> EntryHeader {
|
||||
EntryHeader(Value::Table(t))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn build_default_header() -> Value { // BTreeMap<String, Value>
|
||||
let mut m = BTreeMap::new();
|
||||
|
||||
m.insert(String::from("imag"), {
|
||||
let mut imag_map = BTreeMap::<String, Value>::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<Table> {
|
||||
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 +966,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 +975,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<String, Value>
|
||||
Value::default_header()
|
||||
}
|
||||
|
||||
pub fn from_reader<S: IntoStoreId>(loc: S, file: &mut Read) -> Result<Entry> {
|
||||
let text = {
|
||||
let mut s = String::new();
|
||||
|
@ -1187,14 +1019,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 +1034,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
|
||||
}
|
||||
|
||||
|
@ -1378,7 +1210,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 +1227,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 +1245,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 = {
|
||||
|
|
|
@ -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,109 @@ impl Extract for Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub type EntryResult<T> = RResult<T, ParserError>;
|
||||
|
||||
/// 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<Value>;
|
||||
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<Value> {
|
||||
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::<String, Value>::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<Table> {
|
||||
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)| 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate env_logger;
|
||||
use store::EntryHeader;
|
||||
use super::TomlValueExt;
|
||||
use super::{tokenize, walk_header};
|
||||
use super::Token;
|
||||
|
||||
|
@ -619,11 +723,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 +756,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 +782,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 +819,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 +844,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 +880,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 +895,4 @@ mod test {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue