Merge pull request #149 from matthiasbeyer/libimagstore/header-field-path-util
Add header field addressing by str
This commit is contained in:
commit
0cab0f114d
|
@ -50,7 +50,7 @@ fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) {
|
|||
} else {
|
||||
debug!("Printing header as TOML...");
|
||||
// We have to Value::Table() for Display
|
||||
println!("{}", Value::Table(entry.get_header().toml().clone()))
|
||||
println!("{}", Value::Table(entry.get_header().clone().into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn build_entry_path(rt: &Runtime, path_elem: &str) -> PathBuf {
|
|||
pub fn build_toml_header(matches: &ArgMatches, mut header: EntryHeader) -> EntryHeader {
|
||||
debug!("Building header from cli spec");
|
||||
if let Some(headerspecs) = matches.values_of("header") {
|
||||
let mut main = header.toml_mut();
|
||||
let mut main = header.into();
|
||||
debug!("headerspec = {:?}", headerspecs);
|
||||
let kvs = headerspecs.into_iter()
|
||||
.filter_map(|hs| {
|
||||
|
@ -61,9 +61,13 @@ pub fn build_toml_header(matches: &ArgMatches, mut header: EntryHeader) -> Entry
|
|||
insert_key_into(String::from(current.unwrap()), &mut split, value, &mut main);
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Header = {:?}", main);
|
||||
EntryHeader::from(main)
|
||||
} else {
|
||||
debug!("Header = {:?}", header);
|
||||
header
|
||||
}
|
||||
debug!("Header = {:?}", header);
|
||||
header
|
||||
}
|
||||
|
||||
fn insert_key_into(current: String,
|
||||
|
|
|
@ -25,10 +25,9 @@ impl FieldEq {
|
|||
impl Filter for FieldEq {
|
||||
|
||||
fn filter(&self, e: &Entry) -> bool {
|
||||
let header = e.get_header();
|
||||
self.header_field_path
|
||||
.walk(header)
|
||||
.map(|v| self.expected_value == v.clone())
|
||||
e.get_header()
|
||||
.read(&self.header_field_path[..])
|
||||
.map(|val| val.map(|v| v == self.expected_value).unwrap_or(false))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,7 @@ impl FieldExists {
|
|||
impl Filter for FieldExists {
|
||||
|
||||
fn filter(&self, e: &Entry) -> bool {
|
||||
let header = e.get_header();
|
||||
self.header_field_path.walk(header).is_some()
|
||||
e.get_header().read(&self.header_field_path[..]).is_ok()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,13 +26,12 @@ impl FieldGrep {
|
|||
impl Filter for FieldGrep {
|
||||
|
||||
fn filter(&self, e: &Entry) -> bool {
|
||||
let header = e.get_header();
|
||||
self.header_field_path
|
||||
.walk(header)
|
||||
e.get_header()
|
||||
.read(&self.header_field_path[..])
|
||||
.map(|v| {
|
||||
match v {
|
||||
Value::String(s) => self.grep.captures(&s[..]).is_some(),
|
||||
_ => false,
|
||||
Some(Value::String(s)) => self.grep.captures(&s[..]).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|
|
|
@ -22,18 +22,17 @@ impl FieldIsEmpty {
|
|||
impl Filter for FieldIsEmpty {
|
||||
|
||||
fn filter(&self, e: &Entry) -> bool {
|
||||
let header = e.get_header();
|
||||
self.header_field_path
|
||||
.walk(header)
|
||||
e.get_header()
|
||||
.read(&self.header_field_path[..])
|
||||
.map(|v| {
|
||||
match v {
|
||||
Value::Array(a) => a.is_empty(),
|
||||
Value::Boolean(_) => false,
|
||||
Value::Float(_) => false,
|
||||
Value::Integer(_) => false,
|
||||
Value::String(_) => false,
|
||||
Value::Table(t) => t.is_empty(),
|
||||
_ => true,
|
||||
Some(Value::Array(a)) => a.is_empty(),
|
||||
Some(Value::Boolean(_)) => false,
|
||||
Some(Value::Float(_)) => false,
|
||||
Some(Value::Integer(_)) => false,
|
||||
Some(Value::String(_)) => false,
|
||||
Some(Value::Table(t)) => t.is_empty(),
|
||||
_ => true,
|
||||
}
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|
|
|
@ -50,10 +50,9 @@ impl FieldIsType {
|
|||
impl Filter for FieldIsType {
|
||||
|
||||
fn filter(&self, e: &Entry) -> bool {
|
||||
let header = e.get_header();
|
||||
self.header_field_path
|
||||
.walk(header)
|
||||
.map(|v| self.expected_type.matches(&v))
|
||||
e.get_header()
|
||||
.read(&self.header_field_path[..])
|
||||
.map(|val| val.map(|v| self.expected_type.matches(&v)).unwrap_or(false))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
pub type FieldPath = String;
|
|
@ -1,47 +0,0 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::Error as FmtError;
|
||||
|
||||
use toml::Value;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FieldPathElement {
|
||||
name: String
|
||||
}
|
||||
|
||||
impl FieldPathElement {
|
||||
|
||||
pub fn new(name: String) -> FieldPathElement {
|
||||
FieldPathElement { name: name }
|
||||
}
|
||||
|
||||
pub fn apply(&self, value: Value) -> Option<Value> {
|
||||
use std::str::FromStr;
|
||||
use std::ops::Index;
|
||||
|
||||
match value {
|
||||
Value::Table(t) => {
|
||||
t.get(&self.name).map(|a| a.clone())
|
||||
},
|
||||
|
||||
Value::Array(a) => {
|
||||
usize::from_str(&self.name[..])
|
||||
.ok()
|
||||
.and_then(|i| Some(a[i].clone()))
|
||||
},
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for FieldPathElement {
|
||||
|
||||
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
|
||||
try!(write!(fmt, "{}", self.name));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::Error as FmtError;
|
||||
use std::error::Error;
|
||||
|
||||
use builtin::header::field_path::element::FieldPathElement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FieldPathParsingError {
|
||||
source: String,
|
||||
token: FieldPathElement
|
||||
}
|
||||
|
||||
impl FieldPathParsingError {
|
||||
|
||||
pub fn new(source: String, token: FieldPathElement) -> FieldPathParsingError {
|
||||
FieldPathParsingError { source: source, token: token }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FieldPathParsingError {
|
||||
|
||||
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
|
||||
try!(write!(fmt, "Failed to compile '{}', failed at: '{}'", self.source, self.token));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Error for FieldPathParsingError {
|
||||
|
||||
fn description(&self) -> &str {
|
||||
&self.source[..]
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
None
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::Error as FmtError;
|
||||
use std::error::Error;
|
||||
|
||||
use toml::Value;
|
||||
|
||||
pub mod element;
|
||||
pub mod error;
|
||||
|
||||
use libimagstore::store::Entry;
|
||||
use libimagstore::store::EntryHeader;
|
||||
|
||||
use builtin::header::field_path::element::FieldPathElement;
|
||||
use builtin::header::field_path::error::FieldPathParsingError;
|
||||
|
||||
pub struct FieldPath {
|
||||
elements: Vec<FieldPathElement>,
|
||||
}
|
||||
|
||||
impl FieldPath {
|
||||
|
||||
pub fn new(elements: Vec<FieldPathElement>) -> FieldPath {
|
||||
FieldPath {
|
||||
elements: elements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(source: String) -> Result<FieldPath, FieldPathParsingError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn walk(&self, e: &EntryHeader) -> Option<Value> {
|
||||
let init_val : Value = Value::Table(e.toml().clone());
|
||||
|
||||
self.elements
|
||||
.clone()
|
||||
.into_iter()
|
||||
.fold(Some(init_val), |acc: Option<Value>, token: FieldPathElement| {
|
||||
acc.and_then(|element| token.apply(element))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ version = "0.1.0"
|
|||
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.3.5"
|
||||
fs2 = "0.2.2"
|
||||
glob = "0.2.10"
|
||||
log = "0.3.5"
|
||||
regex = "0.1.47"
|
||||
semver = "0.2"
|
||||
toml = "0.1.25"
|
||||
|
@ -14,3 +14,5 @@ version = "2.0.1"
|
|||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3.4"
|
||||
env_logger = "0.3"
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@ pub enum StoreErrorKind {
|
|||
EntryAlreadyBorrowed,
|
||||
EntryAlreadyExists,
|
||||
MalformedEntry,
|
||||
HeaderPathSyntaxError,
|
||||
HeaderPathTypeFailure,
|
||||
HeaderKeyNotFound,
|
||||
HeaderTypeFailure,
|
||||
// maybe more
|
||||
}
|
||||
|
||||
|
@ -44,6 +48,10 @@ fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str {
|
|||
&StoreErrorKind::EntryAlreadyBorrowed => "Entry is already borrowed",
|
||||
&StoreErrorKind::EntryAlreadyExists => "Entry already exists",
|
||||
&StoreErrorKind::MalformedEntry => "Entry has invalid formatting, missing header",
|
||||
&StoreErrorKind::HeaderPathSyntaxError => "Syntax error in accessor string",
|
||||
&StoreErrorKind::HeaderPathTypeFailure => "Header has wrong type for path",
|
||||
&StoreErrorKind::HeaderKeyNotFound => "Header Key not found",
|
||||
&StoreErrorKind::HeaderTypeFailure => "Header type is wrong",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#[macro_use] extern crate version;
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate version;
|
||||
extern crate fs2;
|
||||
extern crate glob;
|
||||
extern crate regex;
|
||||
|
|
|
@ -7,6 +7,8 @@ use std::sync::Arc;
|
|||
use std::sync::RwLock;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::convert::From;
|
||||
use std::convert::Into;
|
||||
|
||||
use toml::{Table, Value};
|
||||
use regex::Regex;
|
||||
|
@ -327,11 +329,17 @@ pub type EntryContent = String;
|
|||
*/
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EntryHeader {
|
||||
toml: Table,
|
||||
header: Value,
|
||||
}
|
||||
|
||||
pub type EntryResult<V> = RResult<V, ParserError>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum Token {
|
||||
Key(String),
|
||||
Index(usize),
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper type around file header (TOML) object
|
||||
*/
|
||||
|
@ -339,27 +347,16 @@ impl EntryHeader {
|
|||
|
||||
pub fn new() -> EntryHeader {
|
||||
EntryHeader {
|
||||
toml: build_default_header()
|
||||
header: build_default_header()
|
||||
}
|
||||
}
|
||||
|
||||
fn from_table(t: Table) -> EntryHeader {
|
||||
EntryHeader {
|
||||
toml: t
|
||||
header: Value::Table(t)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table which lives in the background
|
||||
*/
|
||||
pub fn toml(&self) -> &Table {
|
||||
&self.toml
|
||||
}
|
||||
|
||||
pub fn toml_mut(&mut self) -> &mut Table {
|
||||
&mut self.toml
|
||||
}
|
||||
|
||||
pub fn parse(s: &str) -> EntryResult<EntryHeader> {
|
||||
use toml::Parser;
|
||||
|
||||
|
@ -371,12 +368,405 @@ impl EntryHeader {
|
|||
}
|
||||
|
||||
pub fn verify(&self) -> Result<()> {
|
||||
verify_header(&self.toml)
|
||||
match &self.header {
|
||||
&Value::Table(ref t) => verify_header(&t),
|
||||
_ => Err(StoreError::new(StoreErrorKind::HeaderTypeFailure, None)),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a header field by a string-spec
|
||||
*
|
||||
* ```ignore
|
||||
* insert("something.in.a.field", Boolean(true));
|
||||
* ```
|
||||
*
|
||||
* If an array field was accessed which is _out of bounds_ of the array available, the element
|
||||
* is appended to the array.
|
||||
*
|
||||
* Inserts a Boolean in the section "something" -> "in" -> "a" -> "field"
|
||||
* A JSON equivalent would be
|
||||
*
|
||||
* {
|
||||
* something: {
|
||||
* in: {
|
||||
* a: {
|
||||
* field: true
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Returns true if header field was set, false if there is already a value
|
||||
*/
|
||||
pub fn insert(&mut self, spec: &str, v: Value) -> Result<bool> {
|
||||
self.insert_with_sep(spec, '.', v)
|
||||
}
|
||||
|
||||
pub fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool> {
|
||||
let tokens = EntryHeader::tokenize(spec, sep);
|
||||
if tokens.is_err() { // return parser error if any
|
||||
return tokens.map(|_| false);
|
||||
}
|
||||
let tokens = tokens.unwrap();
|
||||
|
||||
let destination = tokens.iter().last();
|
||||
if destination.is_none() {
|
||||
return Err(StoreError::new(StoreErrorKind::HeaderPathSyntaxError, None));
|
||||
}
|
||||
let destination = destination.unwrap();
|
||||
|
||||
let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
|
||||
let mut value = EntryHeader::walk_header(&mut self.header, path_to_dest); // walk N-1 tokens
|
||||
if value.is_err() {
|
||||
return value.map(|_| false);
|
||||
}
|
||||
let mut value = value.unwrap();
|
||||
|
||||
// There is already an value at this place
|
||||
if EntryHeader::extract(value, destination).is_ok() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
match destination {
|
||||
&Token::Key(ref s) => { // if the destination shall be an map key
|
||||
match value {
|
||||
/*
|
||||
* Put it in there if we have a map
|
||||
*/
|
||||
&mut Value::Table(ref mut t) => {
|
||||
t.insert(s.clone(), v);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail if there is no map here
|
||||
*/
|
||||
_ => return Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None)),
|
||||
}
|
||||
},
|
||||
|
||||
&Token::Index(i) => { // if the destination shall be an array
|
||||
match value {
|
||||
|
||||
/*
|
||||
* Put it in there if we have an array
|
||||
*/
|
||||
&mut Value::Array(ref mut a) => {
|
||||
a.push(v); // push to the end of the array
|
||||
|
||||
// if the index is inside the array, we swap-remove the element at this
|
||||
// index
|
||||
if a.len() < i {
|
||||
a.swap_remove(i);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Fail if there is no array here
|
||||
*/
|
||||
_ => return Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None)),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header field by a string-spec
|
||||
*
|
||||
* ```ignore
|
||||
* set("something.in.a.field", Boolean(true));
|
||||
* ```
|
||||
*
|
||||
* Sets a Boolean in the section "something" -> "in" -> "a" -> "field"
|
||||
* A JSON equivalent would be
|
||||
*
|
||||
* {
|
||||
* something: {
|
||||
* in: {
|
||||
* a: {
|
||||
* field: true
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* If there is already a value at this place, this value will be overridden and the old value
|
||||
* will be returned
|
||||
*/
|
||||
pub fn set(&mut self, spec: &str, v: Value) -> Result<Option<Value>> {
|
||||
self.set_with_sep(spec, '.', v)
|
||||
}
|
||||
|
||||
pub fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>> {
|
||||
let tokens = EntryHeader::tokenize(spec, sep);
|
||||
if tokens.is_err() { // return parser error if any
|
||||
return Err(tokens.err().unwrap());
|
||||
}
|
||||
let tokens = tokens.unwrap();
|
||||
debug!("tokens = {:?}", tokens);
|
||||
|
||||
let destination = tokens.iter().last();
|
||||
if destination.is_none() {
|
||||
return Err(StoreError::new(StoreErrorKind::HeaderPathSyntaxError, None));
|
||||
}
|
||||
let destination = destination.unwrap();
|
||||
debug!("destination = {:?}", destination);
|
||||
|
||||
let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
|
||||
let mut value = EntryHeader::walk_header(&mut self.header, path_to_dest); // walk N-1 tokens
|
||||
if value.is_err() {
|
||||
return Err(value.err().unwrap());
|
||||
}
|
||||
let mut value = value.unwrap();
|
||||
debug!("walked value = {:?}", value);
|
||||
|
||||
match destination {
|
||||
&Token::Key(ref s) => { // if the destination shall be an map key->value
|
||||
match value {
|
||||
/*
|
||||
* Put it in there if we have a map
|
||||
*/
|
||||
&mut Value::Table(ref mut t) => {
|
||||
debug!("Matched Key->Table");
|
||||
return Ok(t.insert(s.clone(), v));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail if there is no map here
|
||||
*/
|
||||
_ => {
|
||||
debug!("Matched Key->NON-Table");
|
||||
return Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
&Token::Index(i) => { // if the destination shall be an array
|
||||
match value {
|
||||
|
||||
/*
|
||||
* Put it in there if we have an array
|
||||
*/
|
||||
&mut Value::Array(ref mut a) => {
|
||||
debug!("Matched Index->Array");
|
||||
a.push(v); // push to the end of the array
|
||||
|
||||
// if the index is inside the array, we swap-remove the element at this
|
||||
// index
|
||||
if a.len() > i {
|
||||
debug!("Swap-Removing in Array {:?}[{:?}] <- {:?}", a, i, a[a.len()-1]);
|
||||
return Ok(Some(a.swap_remove(i)));
|
||||
}
|
||||
|
||||
debug!("Appended");
|
||||
return Ok(None);
|
||||
},
|
||||
|
||||
/*
|
||||
* Fail if there is no array here
|
||||
*/
|
||||
_ => {
|
||||
debug!("Matched Index->NON-Array");
|
||||
return Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None));
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a header field by a string-spec
|
||||
*
|
||||
* ```ignore
|
||||
* let value = read("something.in.a.field");
|
||||
* ```
|
||||
*
|
||||
* Reads a Value in the section "something" -> "in" -> "a" -> "field"
|
||||
* A JSON equivalent would be
|
||||
*
|
||||
* {
|
||||
* something: {
|
||||
* in: {
|
||||
* a: {
|
||||
* field: true
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* If there is no a value at this place, None will be returned. This also holds true for Arrays
|
||||
* which are accessed at an index which is not yet there, even if the accessed index is much
|
||||
* larger than the array length.
|
||||
*/
|
||||
pub fn read(&self, spec: &str) -> Result<Option<Value>> {
|
||||
self.read_with_sep(spec, '.')
|
||||
}
|
||||
|
||||
pub fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>> {
|
||||
let tokens = EntryHeader::tokenize(spec, splitchr);
|
||||
if tokens.is_err() { // return parser error if any
|
||||
return Err(tokens.err().unwrap());
|
||||
}
|
||||
let tokens = tokens.unwrap();
|
||||
|
||||
let mut header_clone = self.header.clone(); // we clone as READing is simpler this way
|
||||
let mut value = EntryHeader::walk_header(&mut header_clone, tokens); // walk N-1 tokens
|
||||
if value.is_err() {
|
||||
let e = value.err().unwrap();
|
||||
match e.err_type() {
|
||||
// We cannot find the header key, as there is no path to it
|
||||
StoreErrorKind::HeaderKeyNotFound => return Ok(None),
|
||||
_ => return Err(e),
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
Ok(Some(value.unwrap().clone()))
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, spec: &str) -> Result<Option<Value>> {
|
||||
let tokens = EntryHeader::tokenize(spec, '.');
|
||||
if tokens.is_err() { // return parser error if any
|
||||
return Err(tokens.err().unwrap());
|
||||
}
|
||||
let tokens = tokens.unwrap();
|
||||
|
||||
let destination = tokens.iter().last();
|
||||
if destination.is_none() {
|
||||
return Err(StoreError::new(StoreErrorKind::HeaderPathSyntaxError, None));
|
||||
}
|
||||
let destination = destination.unwrap();
|
||||
debug!("destination = {:?}", destination);
|
||||
|
||||
let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
|
||||
let mut value = EntryHeader::walk_header(&mut self.header, path_to_dest); // walk N-1 tokens
|
||||
if value.is_err() {
|
||||
return Err(value.err().unwrap());
|
||||
}
|
||||
let mut value = value.unwrap();
|
||||
debug!("walked value = {:?}", value);
|
||||
|
||||
match destination {
|
||||
&Token::Key(ref s) => { // if the destination shall be an map key->value
|
||||
match value {
|
||||
&mut Value::Table(ref mut t) => {
|
||||
debug!("Matched Key->Table, removing {:?}", s);
|
||||
return Ok(t.remove(s));
|
||||
},
|
||||
_ => {
|
||||
debug!("Matched Key->NON-Table");
|
||||
return Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
&Token::Index(i) => { // if the destination shall be an array
|
||||
match value {
|
||||
&mut Value::Array(ref mut a) => {
|
||||
// if the index is inside the array, we swap-remove the element at this
|
||||
// index
|
||||
if a.len() > i {
|
||||
debug!("Removing in Array {:?}[{:?}]", a, i);
|
||||
return Ok(Some(a.remove(i)));
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
debug!("Matched Index->NON-Array");
|
||||
return Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None));
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn tokenize(spec: &str, splitchr: char) -> Result<Vec<Token>> {
|
||||
use std::str::FromStr;
|
||||
|
||||
spec.split(splitchr)
|
||||
.map(|s| {
|
||||
usize::from_str(s)
|
||||
.map(Token::Index)
|
||||
.or_else(|_| Ok(Token::Key(String::from(s))))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn walk_header(v: &mut Value, tokens: Vec<Token>) -> Result<&mut Value> {
|
||||
use std::vec::IntoIter;
|
||||
|
||||
fn walk_iter<'a>(v: Result<&'a mut Value>, i: &mut IntoIter<Token>) -> Result<&'a mut Value> {
|
||||
let next = i.next();
|
||||
v.and_then(move |value| {
|
||||
if let Some(token) = next {
|
||||
walk_iter(EntryHeader::extract(value, &token), i)
|
||||
} else {
|
||||
Ok(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
walk_iter(Ok(v), &mut tokens.into_iter())
|
||||
}
|
||||
|
||||
fn extract_from_table<'a>(v: &'a mut Value, s: &String) -> Result<&'a mut Value> {
|
||||
match v {
|
||||
&mut Value::Table(ref mut t) => {
|
||||
t.get_mut(&s[..])
|
||||
.ok_or(StoreError::new(StoreErrorKind::HeaderKeyNotFound, None))
|
||||
},
|
||||
_ => Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_from_array(v: &mut Value, i: usize) -> Result<&mut Value> {
|
||||
match v {
|
||||
&mut Value::Array(ref mut a) => {
|
||||
if a.len() < i {
|
||||
Err(StoreError::new(StoreErrorKind::HeaderKeyNotFound, None))
|
||||
} else {
|
||||
Ok(&mut a[i])
|
||||
}
|
||||
},
|
||||
_ => Err(StoreError::new(StoreErrorKind::HeaderPathTypeFailure, None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn extract<'a>(v: &'a mut Value, token: &Token) -> Result<&'a mut Value> {
|
||||
match token {
|
||||
&Token::Key(ref s) => EntryHeader::extract_from_table(v, s),
|
||||
&Token::Index(i) => EntryHeader::extract_from_array(v, i),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn build_default_header() -> BTreeMap<String, Value> {
|
||||
impl Into<Table> for EntryHeader {
|
||||
|
||||
fn into(self) -> Table {
|
||||
match self.header {
|
||||
Value::Table(t) => t,
|
||||
_ => panic!("EntryHeader is not a table!"),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl From<Table> for EntryHeader {
|
||||
|
||||
fn from(t: Table) -> EntryHeader {
|
||||
EntryHeader { header: Value::Table(t) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn build_default_header() -> Value { // BTreeMap<String, Value>
|
||||
let mut m = BTreeMap::new();
|
||||
|
||||
m.insert(String::from("imag"), {
|
||||
|
@ -388,7 +778,7 @@ fn build_default_header() -> BTreeMap<String, Value> {
|
|||
Value::Table(imag_map)
|
||||
});
|
||||
|
||||
m
|
||||
Value::Table(m)
|
||||
}
|
||||
fn verify_header(t: &Table) -> Result<()> {
|
||||
if !has_main_section(t) {
|
||||
|
@ -512,7 +902,7 @@ impl Entry {
|
|||
|
||||
pub fn to_str(&self) -> String {
|
||||
format!("---{header}---\n{content}",
|
||||
header = ::toml::encode_str(&self.header.toml),
|
||||
header = ::toml::encode_str(&self.header.header),
|
||||
content = self.content)
|
||||
}
|
||||
|
||||
|
@ -545,7 +935,11 @@ impl Entry {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate env_logger;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use super::EntryHeader;
|
||||
use super::Token;
|
||||
|
||||
use toml::Value;
|
||||
|
||||
|
@ -696,7 +1090,455 @@ Hai";
|
|||
assert_eq!(TEST_ENTRY, string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_simple() {
|
||||
let tokens = EntryHeader::tokenize("a", '.').unwrap();
|
||||
assert!(tokens.len() == 1, "1 token was expected, {} were parsed", tokens.len());
|
||||
assert!(tokens.iter().next().unwrap() == &Token::Key(String::from("a")),
|
||||
"'a' token was expected, {:?} was parsed", tokens.iter().next());
|
||||
|
||||
let mut header = BTreeMap::new();
|
||||
header.insert(String::from("a"), Value::Integer(1));
|
||||
|
||||
let mut v_header = Value::Table(header);
|
||||
let res = EntryHeader::walk_header(&mut v_header, tokens);
|
||||
assert_eq!(&mut Value::Integer(1), res.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_with_array() {
|
||||
let tokens = EntryHeader::tokenize("a.0", '.').unwrap();
|
||||
assert!(tokens.len() == 2, "2 token was expected, {} were parsed", tokens.len());
|
||||
assert!(tokens.iter().next().unwrap() == &Token::Key(String::from("a")),
|
||||
"'a' token was expected, {:?} was parsed", tokens.iter().next());
|
||||
|
||||
let mut header = BTreeMap::new();
|
||||
let ary = Value::Array(vec![Value::Integer(1)]);
|
||||
header.insert(String::from("a"), ary);
|
||||
|
||||
|
||||
let mut v_header = Value::Table(header);
|
||||
let res = EntryHeader::walk_header(&mut v_header, tokens);
|
||||
assert_eq!(&mut Value::Integer(1), res.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_extract_array() {
|
||||
let tokens = EntryHeader::tokenize("a", '.').unwrap();
|
||||
assert!(tokens.len() == 1, "1 token was expected, {} were parsed", tokens.len());
|
||||
assert!(tokens.iter().next().unwrap() == &Token::Key(String::from("a")),
|
||||
"'a' token was expected, {:?} was parsed", tokens.iter().next());
|
||||
|
||||
let mut header = BTreeMap::new();
|
||||
let ary = Value::Array(vec![Value::Integer(1)]);
|
||||
header.insert(String::from("a"), ary);
|
||||
|
||||
let mut v_header = Value::Table(header);
|
||||
let res = EntryHeader::walk_header(&mut v_header, tokens);
|
||||
assert_eq!(&mut Value::Array(vec![Value::Integer(1)]), res.unwrap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a big testing header.
|
||||
*
|
||||
* JSON equivalent:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "a": {
|
||||
* "array": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
* },
|
||||
* "b": {
|
||||
* "array": [ "string1", "string2", "string3", "string4" ]
|
||||
* },
|
||||
* "c": {
|
||||
* "array": [ 1, "string2", 3, "string4" ]
|
||||
* },
|
||||
* "d": {
|
||||
* "array": [
|
||||
* {
|
||||
* "d1": 1
|
||||
* },
|
||||
* {
|
||||
* "d2": 2
|
||||
* },
|
||||
* {
|
||||
* "d3": 3
|
||||
* },
|
||||
* ],
|
||||
*
|
||||
* "something": "else",
|
||||
*
|
||||
* "and": {
|
||||
* "something": {
|
||||
* "totally": "different"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The sections "a", "b", "c", "d" are created in the respective helper functions
|
||||
* create_header_section_a, create_header_section_b, create_header_section_c and
|
||||
* create_header_section_d.
|
||||
*
|
||||
* These functions can also be used for testing.
|
||||
*
|
||||
*/
|
||||
fn create_header() -> Value {
|
||||
let a = create_header_section_a();
|
||||
let b = create_header_section_b();
|
||||
let c = create_header_section_c();
|
||||
let d = create_header_section_d();
|
||||
|
||||
let mut header = BTreeMap::new();
|
||||
header.insert(String::from("a"), a);
|
||||
header.insert(String::from("b"), b);
|
||||
header.insert(String::from("c"), c);
|
||||
header.insert(String::from("d"), d);
|
||||
|
||||
Value::Table(header)
|
||||
}
|
||||
|
||||
fn create_header_section_a() -> Value {
|
||||
// 0..10 is exclusive 10
|
||||
let a_ary = Value::Array((0..10).map(|x| Value::Integer(x)).collect());
|
||||
|
||||
let mut a_obj = BTreeMap::new();
|
||||
a_obj.insert(String::from("array"), a_ary);
|
||||
Value::Table(a_obj)
|
||||
}
|
||||
|
||||
fn create_header_section_b() -> Value {
|
||||
let b_ary = Value::Array((0..9)
|
||||
.map(|x| Value::String(format!("string{}", x)))
|
||||
.collect());
|
||||
|
||||
let mut b_obj = BTreeMap::new();
|
||||
b_obj.insert(String::from("array"), b_ary);
|
||||
Value::Table(b_obj)
|
||||
}
|
||||
|
||||
fn create_header_section_c() -> Value {
|
||||
let c_ary = Value::Array(
|
||||
vec![
|
||||
Value::Integer(1),
|
||||
Value::String(String::from("string2")),
|
||||
Value::Integer(3),
|
||||
Value::String(String::from("string4"))
|
||||
]);
|
||||
|
||||
let mut c_obj = BTreeMap::new();
|
||||
c_obj.insert(String::from("array"), c_ary);
|
||||
Value::Table(c_obj)
|
||||
}
|
||||
|
||||
fn create_header_section_d() -> Value {
|
||||
let d_ary = Value::Array(
|
||||
vec![
|
||||
{
|
||||
let mut tab = BTreeMap::new();
|
||||
tab.insert(String::from("d1"), Value::Integer(1));
|
||||
tab
|
||||
},
|
||||
{
|
||||
let mut tab = BTreeMap::new();
|
||||
tab.insert(String::from("d2"), Value::Integer(2));
|
||||
tab
|
||||
},
|
||||
{
|
||||
let mut tab = BTreeMap::new();
|
||||
tab.insert(String::from("d3"), Value::Integer(3));
|
||||
tab
|
||||
},
|
||||
].into_iter().map(Value::Table).collect());
|
||||
|
||||
let and_obj = Value::Table({
|
||||
let mut tab = BTreeMap::new();
|
||||
let something_tab = Value::Table({
|
||||
let mut tab = BTreeMap::new();
|
||||
tab.insert(String::from("totally"), Value::String(String::from("different")));
|
||||
tab
|
||||
});
|
||||
tab.insert(String::from("something"), something_tab);
|
||||
tab
|
||||
});
|
||||
|
||||
let mut d_obj = BTreeMap::new();
|
||||
d_obj.insert(String::from("array"), d_ary);
|
||||
d_obj.insert(String::from("something"), Value::String(String::from("else")));
|
||||
d_obj.insert(String::from("and"), and_obj);
|
||||
Value::Table(d_obj)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_big_a() {
|
||||
test_walk_header_extract_section("a", &create_header_section_a());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_big_b() {
|
||||
test_walk_header_extract_section("b", &create_header_section_b());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_big_c() {
|
||||
test_walk_header_extract_section("c", &create_header_section_c());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_big_d() {
|
||||
test_walk_header_extract_section("d", &create_header_section_d());
|
||||
}
|
||||
|
||||
fn test_walk_header_extract_section(secname: &str, expected: &Value) {
|
||||
let tokens = EntryHeader::tokenize(secname, '.').unwrap();
|
||||
assert!(tokens.len() == 1, "1 token was expected, {} were parsed", tokens.len());
|
||||
assert!(tokens.iter().next().unwrap() == &Token::Key(String::from(secname)),
|
||||
"'{}' token was expected, {:?} was parsed", secname, tokens.iter().next());
|
||||
|
||||
let mut header = create_header();
|
||||
let res = EntryHeader::walk_header(&mut header, tokens);
|
||||
assert_eq!(expected, res.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_header_extract_numbers() {
|
||||
test_extract_number("a", 0, 0);
|
||||
test_extract_number("a", 1, 1);
|
||||
test_extract_number("a", 2, 2);
|
||||
test_extract_number("a", 3, 3);
|
||||
test_extract_number("a", 4, 4);
|
||||
test_extract_number("a", 5, 5);
|
||||
test_extract_number("a", 6, 6);
|
||||
test_extract_number("a", 7, 7);
|
||||
test_extract_number("a", 8, 8);
|
||||
test_extract_number("a", 9, 9);
|
||||
|
||||
test_extract_number("c", 0, 1);
|
||||
test_extract_number("c", 2, 3);
|
||||
}
|
||||
|
||||
fn test_extract_number(sec: &str, idx: usize, exp: i64) {
|
||||
let tokens = EntryHeader::tokenize(&format!("{}.array.{}", sec, idx)[..], '.').unwrap();
|
||||
assert!(tokens.len() == 3, "3 token was expected, {} were parsed", tokens.len());
|
||||
{
|
||||
let mut iter = tokens.iter();
|
||||
|
||||
let tok = iter.next().unwrap();
|
||||
let exp = Token::Key(String::from(sec));
|
||||
assert!(tok == &exp, "'{}' token was expected, {:?} was parsed", sec, tok);
|
||||
|
||||
let tok = iter.next().unwrap();
|
||||
let exp = Token::Key(String::from("array"));
|
||||
assert!(tok == &exp, "'array' token was expected, {:?} was parsed", tok);
|
||||
|
||||
let tok = iter.next().unwrap();
|
||||
let exp = Token::Index(idx);
|
||||
assert!(tok == &exp, "'{}' token was expected, {:?} was parsed", idx, tok);
|
||||
}
|
||||
|
||||
let mut header = create_header();
|
||||
let res = EntryHeader::walk_header(&mut header, tokens);
|
||||
assert_eq!(&mut Value::Integer(exp), res.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_read() {
|
||||
let v = create_header();
|
||||
let h = match v {
|
||||
Value::Table(t) => EntryHeader::from_table(t),
|
||||
_ => panic!("create_header() doesn't return a table!"),
|
||||
};
|
||||
|
||||
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 });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("a.array.1") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("a.array.9") { true } else { false });
|
||||
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("c") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Array(_))) = h.read("c.array") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::String(_))) = h.read("c.array.1") { true } else { false });
|
||||
assert!(if let Ok(None) = h.read("c.array.9") { true } else { false });
|
||||
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("d.array.0.d1") { true } else { false });
|
||||
assert!(if let Ok(None) = h.read("d.array.0.d2") { true } else { false });
|
||||
assert!(if let Ok(None) = h.read("d.array.0.d3") { true } else { false });
|
||||
|
||||
assert!(if let Ok(None) = h.read("d.array.1.d1") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("d.array.1.d2") { true } else { false });
|
||||
assert!(if let Ok(None) = h.read("d.array.1.d3") { true } else { false });
|
||||
|
||||
assert!(if let Ok(None) = h.read("d.array.2.d1") { true } else { false });
|
||||
assert!(if let Ok(None) = h.read("d.array.2.d2") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("d.array.2.d3") { true } else { false });
|
||||
|
||||
assert!(if let Ok(Some(Value::String(_))) = h.read("d.something") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("d.and") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("d.and.something") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::String(_))) = h.read("d.and.something.totally") { true } else { false });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_set_override() {
|
||||
let _ = env_logger::init();
|
||||
let v = create_header();
|
||||
let mut h = match v {
|
||||
Value::Table(t) => EntryHeader::from_table(t),
|
||||
_ => panic!("create_header() doesn't return a table!"),
|
||||
};
|
||||
|
||||
println!("Testing index 0");
|
||||
assert_eq!(h.read("a.array.0").unwrap().unwrap(), Value::Integer(0));
|
||||
|
||||
println!("Altering index 0");
|
||||
assert_eq!(h.set("a.array.0", Value::Integer(42)).unwrap().unwrap(), Value::Integer(0));
|
||||
|
||||
println!("Values now: {:?}", h);
|
||||
|
||||
println!("Testing all indexes");
|
||||
assert_eq!(h.read("a.array.0").unwrap().unwrap(), Value::Integer(42));
|
||||
assert_eq!(h.read("a.array.1").unwrap().unwrap(), Value::Integer(1));
|
||||
assert_eq!(h.read("a.array.2").unwrap().unwrap(), Value::Integer(2));
|
||||
assert_eq!(h.read("a.array.3").unwrap().unwrap(), Value::Integer(3));
|
||||
assert_eq!(h.read("a.array.4").unwrap().unwrap(), Value::Integer(4));
|
||||
assert_eq!(h.read("a.array.5").unwrap().unwrap(), Value::Integer(5));
|
||||
assert_eq!(h.read("a.array.6").unwrap().unwrap(), Value::Integer(6));
|
||||
assert_eq!(h.read("a.array.7").unwrap().unwrap(), Value::Integer(7));
|
||||
assert_eq!(h.read("a.array.8").unwrap().unwrap(), Value::Integer(8));
|
||||
assert_eq!(h.read("a.array.9").unwrap().unwrap(), Value::Integer(9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_set_new() {
|
||||
let _ = env_logger::init();
|
||||
let v = create_header();
|
||||
let mut h = match v {
|
||||
Value::Table(t) => EntryHeader::from_table(t),
|
||||
_ => panic!("create_header() doesn't return a table!"),
|
||||
};
|
||||
|
||||
assert!(h.read("a.foo").is_ok());
|
||||
assert!(h.read("a.foo").unwrap().is_none());
|
||||
|
||||
{
|
||||
let v = h.set("a.foo", Value::Integer(42));
|
||||
assert!(v.is_ok());
|
||||
assert!(v.unwrap().is_none());
|
||||
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("a") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("a.foo") { true } else { false });
|
||||
}
|
||||
|
||||
{
|
||||
let v = h.set("new", Value::Table(BTreeMap::new()));
|
||||
assert!(v.is_ok());
|
||||
assert!(v.unwrap().is_none());
|
||||
|
||||
let v = h.set("new.subset", Value::Table(BTreeMap::new()));
|
||||
assert!(v.is_ok());
|
||||
assert!(v.unwrap().is_none());
|
||||
|
||||
let v = h.set("new.subset.dest", Value::Integer(1337));
|
||||
assert!(v.is_ok());
|
||||
assert!(v.unwrap().is_none());
|
||||
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("new") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("new.subset") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("new.subset.dest") { true } else { false });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_header_insert_override() {
|
||||
let _ = env_logger::init();
|
||||
let v = create_header();
|
||||
let mut h = match v {
|
||||
Value::Table(t) => EntryHeader::from_table(t),
|
||||
_ => panic!("create_header() doesn't return a table!"),
|
||||
};
|
||||
|
||||
println!("Testing index 0");
|
||||
assert_eq!(h.read("a.array.0").unwrap().unwrap(), Value::Integer(0));
|
||||
|
||||
println!("Altering index 0");
|
||||
assert_eq!(h.insert("a.array.0", Value::Integer(42)).unwrap(), false);
|
||||
println!("...should have failed");
|
||||
|
||||
println!("Testing all indexes");
|
||||
assert_eq!(h.read("a.array.0").unwrap().unwrap(), Value::Integer(0));
|
||||
assert_eq!(h.read("a.array.1").unwrap().unwrap(), Value::Integer(1));
|
||||
assert_eq!(h.read("a.array.2").unwrap().unwrap(), Value::Integer(2));
|
||||
assert_eq!(h.read("a.array.3").unwrap().unwrap(), Value::Integer(3));
|
||||
assert_eq!(h.read("a.array.4").unwrap().unwrap(), Value::Integer(4));
|
||||
assert_eq!(h.read("a.array.5").unwrap().unwrap(), Value::Integer(5));
|
||||
assert_eq!(h.read("a.array.6").unwrap().unwrap(), Value::Integer(6));
|
||||
assert_eq!(h.read("a.array.7").unwrap().unwrap(), Value::Integer(7));
|
||||
assert_eq!(h.read("a.array.8").unwrap().unwrap(), Value::Integer(8));
|
||||
assert_eq!(h.read("a.array.9").unwrap().unwrap(), Value::Integer(9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_insert_new() {
|
||||
let _ = env_logger::init();
|
||||
let v = create_header();
|
||||
let mut h = match v {
|
||||
Value::Table(t) => EntryHeader::from_table(t),
|
||||
_ => panic!("create_header() doesn't return a table!"),
|
||||
};
|
||||
|
||||
assert!(h.read("a.foo").is_ok());
|
||||
assert!(h.read("a.foo").unwrap().is_none());
|
||||
|
||||
{
|
||||
let v = h.insert("a.foo", Value::Integer(42));
|
||||
assert!(v.is_ok());
|
||||
assert_eq!(v.unwrap(), true);
|
||||
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("a") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("a.foo") { true } else { false });
|
||||
}
|
||||
|
||||
{
|
||||
let v = h.insert("new", Value::Table(BTreeMap::new()));
|
||||
assert!(v.is_ok());
|
||||
assert_eq!(v.unwrap(), true);
|
||||
|
||||
let v = h.insert("new.subset", Value::Table(BTreeMap::new()));
|
||||
assert!(v.is_ok());
|
||||
assert_eq!(v.unwrap(), true);
|
||||
|
||||
let v = h.insert("new.subset.dest", Value::Integer(1337));
|
||||
assert!(v.is_ok());
|
||||
assert_eq!(v.unwrap(), true);
|
||||
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("new") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.read("new.subset") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("new.subset.dest") { true } else { false });
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_delete() {
|
||||
let _ = env_logger::init();
|
||||
let v = create_header();
|
||||
let mut h = match v {
|
||||
Value::Table(t) => EntryHeader::from_table(t),
|
||||
_ => panic!("create_header() doesn't return a table!"),
|
||||
};
|
||||
|
||||
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 });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("a.array.1") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(_))) = h.read("a.array.9") { true } else { false });
|
||||
|
||||
assert!(if let Ok(Some(Value::Integer(1))) = h.delete("a.array.1") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Integer(9))) = h.delete("a.array.8") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Array(_))) = h.delete("a.array") { true } else { false });
|
||||
assert!(if let Ok(Some(Value::Table(_))) = h.delete("a") { true } else { false });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue