6ZJX2OQV5MYICMONYVYP55DUKORJ3KPLIN7LHAYLXY7ZB7SZE7TAC
use std::fmt;
use std::cmp::Ordering;
use std::fmt::Display;
/// A KeyPath it used to follow keys into a keytree.
///
/// For example the keypath `"hobbit::friends"` will select the tree of two
/// hobbits Frodo and Samwise from the following keytree. `"hobbit:age"` will
/// select Frodo's age.
///
/// ```
/// hobbit:
/// name: Frodo Baggins
/// age: 60
/// friends:
/// hobbit:
/// name: Bilbo Baggins
/// age: 111
/// hobbit:
/// name: Samwise Gamgee
/// age: 38
/// nick: Sam
/// ```
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct KeyPath(pub Vec<String>);
impl KeyPath {
// pub (crate) fn new() -> Self {
// KeyPath(Vec::new())
// }
// pub (crate) fn truncate(&mut self, len: usize) {
// self.0.truncate(len);
// }
// pub (crate) fn append(&mut self, other: &mut KeyPath) {
// self.0.append(&mut other.0);
// }
// pub (crate) fn len(&self) -> usize {
// self.0.len()
// }
pub (crate) fn from_str(s: &str) -> Self {
let v = s.split(':')
.filter(|s| !s.is_empty())
.map(|s| String::from(s))
.collect::<Vec<String>>();
KeyPath(v)
}
}
impl fmt::Debug for KeyPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl Display for KeyPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, segment) in self.0.iter().enumerate() {
write!(f, "{}", segment)?;
if i < self.0.len() - 1 { write!(f, "::")? };
};
Ok(())
}
}
impl Ord for KeyPath {
fn cmp(&self, other: &Self) -> Ordering {
for n in 0..self.0.len() {
match self.0[n].cmp(&other.0[n]) {
Ordering::Less => return Ordering::Less,
Ordering::Greater => return Ordering::Greater,
Ordering::Equal => {},
}
}
Ordering::Equal
}
}
impl PartialOrd for KeyPath {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.0.cmp(&other.0))
}
}
#![allow(dead_code)]
use crate::{KeyTree, Token};
use crate::builder::{Parents, SNSibs};
use crate::error::{Error, ErrorKind};
use crate::{Line, Result};
const INDENT_STEP: usize = 4;
// Parser state. Used in parser() function.
#[derive(Clone, Debug, PartialEq)]
enum PS {
FC, // First char.
BK, // Before key.
COK, // Comment or key
IK, // In key.
RAK, // The character right after the key.
AK, // After key.
IV, // In value.
CM, // In comment
}
// Chars received
enum Char {
Char,
Colon,
ForwardSlash,
Newline,
Whitespace,
}
impl Char {
fn from_char(c: char) -> Self {
if c == '\n' { return Char::Newline };
if c.is_whitespace() { return Char::Whitespace };
match c {
':' => Char::Colon,
'/' => Char::ForwardSlash,
_ => Char::Char
}
}
}
#[derive(Debug)]
pub (crate) struct Builder<'a> {
s: &'a str,
tokens: Vec<Token<'a>>,
// Every token at indent n has a parent at indent 0 to n - 1. This parent
parents: Parents,
snsibs: SNSibs,
}
impl<'a> Builder<'a> {
pub fn from_str(s: &'a str) -> Self {
Builder
{
s: s,
tokens: Vec::new(),
parents: Parents::new(),
snsibs: SNSibs::new(),
}
}
fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
pub fn token(&self, ix: usize) -> &Token<'a> {
&self.tokens[ix]
}
// Returns the next() value of ixth token.
pub fn next(&self, ix: usize) -> Option<usize> {
self.token(ix).next()
}
// Move out of Builder into Core.
pub fn to_core(self) -> KeyTree<'a> {
KeyTree {
s: self.s,
tokens: self.tokens,
}
}
// Each token at indent n, except the root token, has parents at each
// indent level from 0 to n-1. To build up links from parents to children
// we keep track of current parents. This is akin to thinking about which
// parents are 'in scope'. We look at each
fn append(&mut self, indent: usize, token: Token<'a>) -> Result<()> {
match (token, self.is_empty()) {
(token @ Token::KeyValue {..}, true) => {
Err(Error::new(ErrorKind::FirstTokenMustBeKey(token.to_string())))
},
(token @ Token::Key {..}, true) => {
self.tokens.push(token);
self.snsibs.push(0);
self.parents.push(0);
Ok(())
},
(token, false) => {
// Possibility A. is that the lastest token (name:) increases
// the indent level by 1.
//
// hobbit:
// name: Frodo
// Possibility B is that the lastest token (name:) has the same
// key name and the same indent level as the previous token.
//
// hobbit:
// name: Frodo
// name: Bilbo
// Possibility C is that the latest token (location:) has
// reduced the indent level.
//
// scenario:
// hobbit:
// friends:
// ..
// name: Frodo
// name: Bilbo
// location: Middle Earth
// Remove out-of-scope parents. For example in example 3. we
// want to remove 'friends:' key as it is no longer in scope.
self.parents.truncate(indent);
// Get parent index.
if indent == 0 {
return Err(Error::new(ErrorKind::NonRootNoIndent(
token.to_string(),
token.line()
)))
};
let parent_ix = self.parents.parent(indent - 1);
// Get the index of the new token, when the token is actually
// pushed to self.tokens.
let child_ix = self.tokens.len();
// Append the child of the parent.
self.tokens[parent_ix].set_child(token.key(), child_ix);
// If token is a Token::Key then token to parents list.
if let Token::Key {..} = token {
self.parents.push(child_ix);
}
// Remove out-of-scope same-name-siblings. For example 'name:'
// in example 3.
self.snsibs.truncate(indent + 1);
// Non-contiguous same-name-siblings will never be found,
// because keys are discovered by iterating through children
// Vec in Token, so any repeats will not be found.
//
// If a same-name-sibling exists then add next() to it.
if let Some(snsib_ix) = self.snsibs.sibling(indent) {
if self.tokens[snsib_ix].key() == token.key() {
self.tokens[snsib_ix].set_next(child_ix);
};
};
// Append token to same-name-siblings list.
self.snsibs.truncate(indent);
self.snsibs.push(child_ix);
// Insert token into Builder.
self.tokens.push(token);
Ok(())
},
}
}
// Parse a `KeyTree` string into an immutable `KeyTreeCore`. For context, see
// main example at the start of the documentation or in README.md
pub fn parse(s: &str) -> Result<KeyTree> {
if s == "" { return Err(Error::new(ErrorKind::EmptyString)) };
let mut parse_state: PS = PS::FC;
let mut line = 0;
let mut line_start = 0;
let mut start_key = 0;
let mut end_key = 0;
let mut start_val = 0;
// let mut end_val;
let mut root_indent = None;
let mut core_builder: Builder = Builder::from_str(s);
for (pos, ch) in core_builder.s.char_indices() {
match (&parse_state, Char::from_char(ch)) {
// Matches are ordered by estimated rate of occurence.
// Whitespace, no errors.
(PS::FC, Char::Whitespace) => {
line_start = pos;
parse_state = PS::BK;
},
(PS::BK, Char::Whitespace) => { },
(PS::CM, Char::Whitespace) => { },
(PS::RAK, Char::Whitespace) => {
parse_state = PS::AK;
},
(PS::AK, Char::Whitespace) => { },
(PS::IV, Char::Whitespace) => { },
// Char, no errors.
(PS::AK, Char::Char) => {
start_val = pos;
parse_state = PS::IV;
},
(PS::IV, Char::Char) => {},
(PS::FC, Char::Newline) => {
line += 1;
parse_state = PS::FC;
},
(PS::FC, Char::Char) => {
line_start = pos;
start_key = pos;
parse_state = PS::IK;
},
(PS::BK, Char::Char) => {
start_key = pos;
parse_state = PS::IK;
},
(PS::COK, Char::Char) => {
parse_state = PS::IK;
},
(PS::CM, Char::Char) => { },
(PS::IK, Char::Char) => {}
// Newline, no errors
(PS::BK, Char::Newline) => {
line += 1;
parse_state = PS::FC;
},
(PS::CM, Char::Newline) => {
line += 1;
parse_state = PS::FC;
},
(PS::RAK, Char::Newline) => {
line += 1;
let token = Token::Key {
key: &s[start_key..=end_key],
children: Vec::new(),
next: None,
line: Line::new(line),
};
let indent = indent(&mut root_indent, line_start, start_key)
.map_err(|e| e.from_inner(&token))?;
core_builder.append(indent, token)?;
parse_state = PS::FC;
},
(PS::AK, Char::Newline) => {
line += 1;
let token = Token::Key {
key: &s[start_key..=pos],
children: Vec::new(),
next: None,
line: Line::new(line),
};
let indent = indent(&mut root_indent, line_start, start_key)
.map_err(|e| e.from_inner(&token))?;
core_builder.append(indent, token)?;
parse_state = PS::FC;
},
(PS::IV, Char::Newline) => {
line += 1;
let token = Token::KeyValue {
key: &s[start_key..=end_key],
value: &s[start_val..=pos - 1],
next: None,
line: Line::new(line),
};
let indent = indent(&mut root_indent, line_start, start_key)
.map_err(|e| e.from_inner(&token))?;
core_builder.append(indent, token)?;
parse_state = PS::FC;
},
// Colon, no errors
(PS::COK, Char::Colon) => {
parse_state = PS::IK;
},
(PS::CM, Char::Colon) => { },
(PS::IV, Char::Colon) => {},
(PS::AK, Char::Colon) => {
start_val = pos;
parse_state = PS::IV;
},
(PS::IK, Char::Colon) => {
end_key = pos - 1;
parse_state = PS::RAK;
}
// Forward slash, no errors
(PS::FC, Char::ForwardSlash) => {
line_start = pos;
start_key = pos;
parse_state = PS::COK;
},
(PS::CM, Char::ForwardSlash) => {},
(PS::BK, Char::ForwardSlash) => {
start_key = pos;
parse_state = PS::COK;
},
(PS::IK, Char::ForwardSlash) => {}
(PS::AK, Char::ForwardSlash) => {
start_val = pos;
parse_state = PS::IV;
},
(PS::IV, Char::ForwardSlash) => {},
// Whitespace errors
(PS::IK, Char::Whitespace) => {
let token_str = &s[start_key..=pos - 1];
return Err(Error::new(ErrorKind::NoColonAfterKey(token_str.to_string())));
},
(PS::COK, Char::Whitespace) => {
let token_str = &s[start_key..=pos - 1];
return Err(Error::new(ErrorKind::IncompleteCommentOrKey(token_str.to_string())));
},
// Char errors
(PS::RAK, Char::Char) => {
return Err(Error::new(ErrorKind::NoSpaceAfterKey));
},
// Newline errors
(PS::COK, Char::Newline) => {
// line += 1;
return Err(Error::new(ErrorKind::IncompleteLine(String::from("/"))))
},
(PS::IK, Char::Newline) => {
// line += 1;
let token_str = &s[start_key..=pos - 1];
return Err(Error::new(ErrorKind::IncompleteLine(token_str.to_string())));
},
// Colon, errors
(PS::FC, Char::Colon) => {
// line_start = pos;
return Err(Error::new(ErrorKind::ColonBeforeKey));
},
(PS::BK, Char::Colon) => {
// start_key = pos;
return Err(Error::new(ErrorKind::ColonBeforeKey));
},
(PS::RAK, Char::Colon) => {
return Err(Error::new(ErrorKind::NoSpaceAfterKey));
},
// Forward slash errors
(PS::COK, Char::ForwardSlash) => {
let token_str = &s[start_key..=pos - 1];
return Err(Error::new(ErrorKind::IncompleteCommentOrKey(token_str.to_string())));
},
(PS::RAK, Char::ForwardSlash) => {
return Err(Error::new(ErrorKind::NoSpaceAfterKey));
},
}; // end match
};
// Handle strings that don't end in '\n".
match parse_state {
PS::FC => {},
PS::BK => {},
PS::COK => {
let token_str = &s[start_key..];
return Err(Error::new(ErrorKind::IncompleteCommentOrKey(token_str.to_string())));
},
PS::IK => {
// line += 1;
let token_str = &s[start_key..];
return Err(Error::new(ErrorKind::IncompleteLine(token_str.to_string())));
},
PS::RAK => {
line += 1;
let token = Token::Key {
key: &s[start_key..s.chars().count() - 1],
children: Vec::new(),
next: None,
line: Line::new(line),
};
let indent = indent(&mut root_indent, line_start, start_key)
.map_err(|e| e.from_inner(&token))?;
core_builder.append(indent, token)?;
},
PS::AK => {
line += 1;
let token = Token::Key {
key: &s[start_key..],
children: Vec::new(),
next: None,
line: Line::new(line),
};
let indent = indent(&mut root_indent, line_start, start_key)
.map_err(|e| e.from_inner(&token))?;
core_builder.append(indent, token)?;
},
PS::IV => {
line += 1;
let token = Token::KeyValue {
key: &s[start_key..=end_key],
value: &s[start_val..],
next: None,
line: Line::new(line),
};
let indent = indent(&mut root_indent, line_start, start_key)
.map_err(|e| e.from_inner(&token))?;
core_builder.append(indent, token)?;
},
PS::CM => {},
};
if core_builder.is_empty() {
Err(Error::new(ErrorKind::NoTokens))
} else {
Ok(core_builder.to_core())
}
}
}
// Returns indent as 0, 1, 2 from token data.
fn indent(
root_indent: &mut Option<usize>,
line_start: usize,
start_key: usize) -> Result<usize>
{
match root_indent {
// root_indent has not been set.
None => {
*root_indent = Some(start_key - line_start);
Ok(0)
},
Some(root_indent) => {
let chars_indent = start_key - line_start - *root_indent;
if chars_indent % INDENT_STEP != 0 {
return Err(Error::new(ErrorKind::InnerBadIndent(chars_indent)));
} else {
Ok(chars_indent / INDENT_STEP)
}
}
}
}
use std::convert::TryInto;
use std::fmt;
pub use crate::error::{Error, ErrorKind};
use crate::path::KeyPath;
use crate::parser::Builder;
mod builder;
pub mod error;
pub mod into;
pub mod parser;
pub mod path;
// impl<'a> Display for Token<'a> {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// match self {
// Token::Key { line, .. } => write!(f, "{}", line),
// Token::KeyValue { line, .. } => write!(f, "{}", line),
// }
// }
// }
type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Copy, Debug)]
pub struct Line(usize);
impl Line {
pub fn new(line: usize) -> Self {
Line(line)
}
}
impl fmt::Display for Line {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
// We could insert position in here as we parse.
#[derive(Clone, Debug)]
pub enum Token<'a> {
Key {
key: &'a str,
children: Vec<(&'a str, usize)>,
next: Option<usize>, // refers to Vec index for now
line: Line, // used for debugging
},
KeyValue {
key: &'a str,
value: &'a str,
next: Option<usize>, // refers to Vec index for now
line: Line, // used for debugging
},
}
impl<'a> Token<'a> {
pub fn line(&self) -> Line {
match self {
Token::Key { line, .. } => *line,
Token::KeyValue { line, .. } => *line,
}
}
pub fn get_child(&self, key: &str) -> Option<usize> {
match self {
Token::KeyValue {..} => None,
Token::Key { children, .. } => {
children.iter()
.find(|(k, _)| &key == k)
.map(|(_, ix)| *ix)
},
}
}
pub fn key(&self) -> &'a str {
match self {
Token::Key {key, ..} => key,
Token::KeyValue {key, ..} => key,
} }
pub fn next(&self) -> Option<usize> {
match self {
Token::Key { next, .. } => {
match next {
Some(next) => Some(*next),
None => None,
}
},
Token::KeyValue { next, .. } => {
match next {
Some(next) => Some(*next),
None => None,
}
},
}
}
// fn new(s: &'a str) -> Self {
// Token::Key {
// key: s,
// children: Vec::new(),
// next: None,
// }
// }
// Set the next iteration of the Token to a token with indext token_i.
pub fn set_next(&mut self, token_i: usize) {
match self {
Token::KeyValue {ref mut next, .. } => {
*next = Some(token_i);
},
Token::Key {ref mut next, .. } => {
*next = Some(token_i);
},
}
}
pub fn set_child(&mut self, key: &'a str, child_ix: usize) {
match self {
Token::KeyValue {..}=> { panic!() },
Token::Key {
children,
..
} => { children.push((key, child_ix)) },
}
}
}
impl<'a> fmt::Display for Token<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Token::Key { key, .. } => write!(f, "{}:", key),
Token::KeyValue { key, value, .. } => write!(f, "{}: {}", key, value),
}
}
}
/// The parsed keytree string.
#[derive(Debug)]
pub struct KeyTree<'a> {
s: &'a str,
tokens: Vec<Token<'a>>,
}
impl<'a> KeyTree<'a> {
/// Returns a lightweight reference to the root of `KeyTree`.
pub fn to_ref(&'a self) -> KeyTreeRef<'a> {
KeyTreeRef(self, 0)
}
/// Returns the key name of the top token.
pub fn name(&self) -> String {
self.tokens[0].key().to_string()
}
/// Parse the keytree string.
pub fn parse(s: &'a str) -> Result<Self> {
Builder::parse(s)
}
}
// impl<'a, T> TryInto<T> for KeyTree<'a> {
// type Error = Error;
//
// fn try_into(self) -> std::result::Result<T, Self::Error> {
// self.to_ref().try_into()
// }
// }
/// A lightweight reference into the parsed keytree string.
#[derive(Clone, Debug)]
pub struct KeyTreeRef<'a>(&'a KeyTree<'a>, usize);
impl<'a> KeyTreeRef<'a> {
pub (crate) fn top_token(&self) -> &'a Token<'a> {
&self.0.tokens[self.1]
}
fn set_cursor(&mut self, ix: usize) {
self.1 = ix;
}
fn next(&mut self) -> Option<()> {
match self.top_token().next() {
Some(ix) => {
self.1 = ix;
return Some(())
},
None => return None,
}
}
/// Coerces the value at key_path from a string to the receiver type.
/// See top page for an example.
pub fn at<T>(&self, key_path: &str) -> Result<T>
where
KeyTreeRef<'a>: TryInto<T>,
KeyTreeRef<'a>: TryInto<T, Error = Error>,
{
let path = KeyPath::from_str(key_path);
let kt = self.recurse(&path)?;
// Check that top token is unique
match kt.top_token().next() {
None => {
kt.try_into()
},
_ => Err(Error::new(ErrorKind::EUniqueTokenFMany)),
}
}
/// Coerces the value at key_path from a string to `Some` of the receiver
/// type, or None if there is nothing at key_path. See top page for example.
pub fn op<T>(&self, key_path: &str) -> Result<Option<T>>
where
KeyTreeRef<'a>: TryInto<T>,
KeyTreeRef<'a>: TryInto<T, Error = Error>,
{
let path = KeyPath::from_str(key_path);
let kt = match self.op_recurse(&path)? {
Some(kt) => kt,
None => return Ok(None),
};
// Check that top token is unique
match kt.top_token().next() {
None => {
// re-wrap
match kt.try_into() {
Ok(t_item) => Ok(Some(t_item)),
Err(e) => Err(e),
}
},
_ => Err(Error::new(ErrorKind::EUniqueTokenFMany)),
}
}
/// Coerces the values at key_path from strings to a `Vec` of the receiver
/// type, or an empty `Vec` if there is nothing at key_path. See top page
/// for example.
pub fn vec<T>(&self, key_path: &str) -> Result<Vec<T>>
where
KeyTreeRef<'a>: TryInto<T>,
KeyTreeRef<'a>: TryInto<T, Error = Error>,
{
let path = KeyPath::from_str(key_path);
match self.op_recurse(&path)? {
Some(mut kt) => {
let mut v = vec!(kt.clone().try_into()?);
while let Some(_) = kt.next() {
v.push(kt.clone().try_into()?);
};
Ok(v)
},
None => Ok(Vec::new()),
}
}
// Follow the path and return the sub-tree.
pub (crate) fn recurse(&self, path: &KeyPath) -> Result<KeyTreeRef<'a>> {
let mut kt = (*self).clone();
let mut iter = path.0.iter();
// Check that the first segment in the path equals the key of the
// current top token.
match iter.next() {
Some(key) => {
if key != self.top_token().key() {
return Err(Error::new(ErrorKind::BadFirstSegment(
self.top_token().to_string(),
self.top_token().line().to_string(),
path.to_string(),
)))
}
},
None => { return Err(Error::new(ErrorKind::EmptyPath)) },
};
// Iterate through the rest of the path and follow the children.
while let Some(key) = iter.next() {
match kt.top_token() {
Token::KeyValue {..} => {
return Err(Error::new(ErrorKind::EKeyFKeyValue))
},
token @ Token::Key {..} => {
match token.get_child(key) {
Some(ix) => { kt.set_cursor(ix) },
None => {
return Err(Error::new(ErrorKind::NoChildWithSegment(
self.top_token().to_string(),
self.top_token().line().to_string(),
key.to_string(),
)))
},
}
}
}
};
Ok(kt)
}
pub (crate) fn op_recurse(&self, path: &KeyPath) -> Result<Option<KeyTreeRef<'a>>> {
match self.recurse(path) {
Ok(kt) => Ok(Some(kt)),
Err(err) => {
match err.kind() {
ErrorKind::NoChildWithSegment(_, _, _) | ErrorKind::EKeyFKeyValue => Ok(None),
_ => Err(err),
}
}
}
}
}
#[cfg(test)]
mod test {
use std::convert::TryInto;
use super::*;
use crate::{KeyTree, KeyTreeRef};
use crate::error::Error;
use crate::path::KeyPath;
static HOBBITS: &'static str = r#"hobbit:
name: Frodo Baggins
age: 60
friends:
hobbit:
name: Bilbo Baggins
age: 111
hobbit:
name: Samwise Gamgee
age: 38
nick: Sam"#;
#[derive(Debug)]
struct Hobbit {
name: String,
age: u32,
friends: Vec<Hobbit>,
nick: Option<String>,
}
impl<'a> TryInto<Hobbit> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<Hobbit> {
Ok(
Hobbit {
name: self.at("hobbit::name")?,
age: self.at("hobbit::age")?,
friends: self.vec("hobbit::friends::hobbit")?,
nick: self.op("hobbit::nick")?,
}
)
}
}
#[test]
fn recurse() {
let keytree = KeyTree::parse(HOBBITS).unwrap();
let kt = keytree.to_ref();
let kt2 = kt.recurse(&KeyPath::from_str("hobbit::age")).unwrap();
assert_eq!(
kt2.top_token().key(),
"age"
);
}
#[test]
fn at() {
// let kt = KeyTree::parse(HOBBITS).unwrap();
// let hobbit: Hobbit = kt.to_ref().try_into().unwrap();
// assert_eq!(
// hobbit.name,
// "Frodo Baggins",
// );
// assert_eq!(
// hobbit.nick,
// None,
// );
}
}
use core::convert::TryInto;
use core::num::{
NonZeroI128,
NonZeroI16,
NonZeroI32,
NonZeroI64,
NonZeroI8,
NonZeroIsize,
NonZeroU128,
NonZeroU16,
NonZeroU32,
NonZeroU64,
NonZeroU8,
NonZeroUsize,
};
use crate::{KeyTreeRef, Token};
use crate::error::{Error, ErrorKind};
use crate::Result;
// use std::iter::FromIterator;
use std::net::{
IpAddr,
Ipv4Addr,
Ipv6Addr,
SocketAddr,
SocketAddrV4,
SocketAddrV6,
};
use std::path::PathBuf;
use std::str::FromStr;
impl<'a> KeyTreeRef<'a> {
pub fn from_str<T: FromStr>(&self, into_type: &str) -> Result<T> {
match self.top_token() {
Token::KeyValue {
next,
value,
..
} => {
match next {
Some(_) => Err(Error::new(ErrorKind::EUniqueKeyValueFMany)),
None => {
match T::from_str(value) {
Err(_) => {
Err(Error::new(ErrorKind::FailedFromStr(
self.top_token().to_string(),
self.top_token().line().to_string(),
into_type.to_string(),
)))
},
Ok(t) => Ok(t),
}
}
}
},
Token::Key { .. } => Err(Error::new(ErrorKind::EKeyValueFKey)),
}
}
}
impl<'a> TryInto<IpAddr> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<IpAddr> {
self.from_str("IP address")
}
}
impl<'a> TryInto<SocketAddr> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<SocketAddr> {
self.from_str("socket address")
}
}
impl<'a> TryInto<bool> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<bool> {
self.from_str("bool")
}
}
impl<'a> TryInto<char> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<char> {
self.from_str("char")
}
}
impl<'a> TryInto<f32> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<f32> {
self.from_str("f32")
}
}
impl<'a> TryInto<f64> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<f64> {
self.from_str("f64")
}
}
impl<'a> TryInto<i128> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<i128> {
self.from_str("i128")
}
}
impl<'a> TryInto<i16> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<i16> {
self.from_str("i16")
}
}
impl<'a> TryInto<i32> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<i32> {
self.from_str("i32")
}
}
impl<'a> TryInto<i64> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<i64> {
self.from_str("i64")
}
}
impl<'a> TryInto<i8> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<i8> {
self.from_str("i8")
}
}
impl<'a> TryInto<isize> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<isize> {
self.from_str("isize")
}
}
impl<'a> TryInto<u128> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<u128> {
self.from_str("u128")
}
}
impl<'a> TryInto<u16> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<u16> {
self.from_str("u16")
}
}
impl<'a> TryInto<u32> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<u32> {
self.from_str("u32")
}
}
impl<'a> TryInto<u64> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<u64> {
self.from_str("u64")
}
}
impl<'a> TryInto<u8> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<u8> {
self.from_str("u8")
}
}
impl<'a> TryInto<usize> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<usize> {
self.from_str("usize")
}
}
impl<'a> TryInto<Ipv4Addr> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<Ipv4Addr> {
self.from_str("IPv4 address")
}
}
impl<'a> TryInto<Ipv6Addr> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<Ipv6Addr> {
self.from_str("IPv6 address")
}
}
impl<'a> TryInto<SocketAddrV4> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<SocketAddrV4> {
self.from_str("IPv4 address")
}
}
impl<'a> TryInto<SocketAddrV6> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<SocketAddrV6> {
self.from_str("IPv6 address")
}
}
impl<'a> TryInto<NonZeroI128> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroI128> {
self.from_str("non-zero i128")
}
}
impl<'a> TryInto<NonZeroI16> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroI16> {
self.from_str("non-zero i16")
}
}
impl<'a> TryInto<NonZeroI32> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroI32> {
self.from_str("non-zero i32")
}
}
impl<'a> TryInto<NonZeroI64> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroI64> {
self.from_str("non-zero i64")
}
}
impl<'a> TryInto<NonZeroI8> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroI8> {
self.from_str("non-zero i8")
}
}
impl<'a> TryInto<NonZeroIsize> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroIsize> {
self.from_str("non-zero isize")
}
}
impl<'a> TryInto<NonZeroU128> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroU128> {
self.from_str("non-zero u128")
}
}
impl<'a> TryInto<NonZeroU16> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroU16> {
self.from_str("non-zero u16")
}
}
impl<'a> TryInto<NonZeroU32> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroU32> {
self.from_str("non-zero u32")
}
}
impl<'a> TryInto<NonZeroU64> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroU64> {
self.from_str("non-zero u64")
}
}
impl<'a> TryInto<NonZeroU8> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroU8> {
self.from_str("non-zero u8")
}
}
impl<'a> TryInto<NonZeroUsize> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<NonZeroUsize> {
self.from_str("non-zero usize")
}
}
impl<'a> TryInto<PathBuf> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<PathBuf> {
self.from_str("path")
}
}
impl<'a> TryInto<String> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<String> {
self.from_str("string")
}
}
use std::error::Error as StdError;
use std::fmt;
use std::fmt::Display;
use std::mem;
use crate::Line;
// `ErrorKind` uses only owned strings or copy types as parameters. This means
// that `Error`s in client code do not require a lifetime, which could cause
// trouble if the error is wrapped and passed around.
#[derive(Clone, Debug)]
pub struct Error(pub Box<ErrorKind>);
impl Error {
pub fn new(kind: ErrorKind) -> Error {
Error(Box::new(kind))
}
pub fn kind(&self) -> ErrorKind {
*self.0.clone()
}
// Maps InnerBadIndent to BadIndent
pub fn from_inner<T: Display>(self, token: T) -> Error {
match self.kind() {
ErrorKind::InnerBadIndent(indent) => {
Error::new(ErrorKind::BadIndent(token.to_string(), indent))
},
_ => self,
}
}
}
#[derive(Clone, Debug)]
pub enum ErrorKind {
// Expected key, found key/value pair.
EKeyFKeyValue,
// Expected key/value pair, found key.
EKeyValueFKey,
// Expected a key/value pair, found many.
EUniqueKeyValueFMany,
EUniqueTokenFMany,
// `FromStr(value)` failed (key/value pair as string, position as string, into_type as string)
FailedFromStr(String, String, String),
NoChildWithSegment(String, String, String),
// Use adjective_noun for error names.
EmptyString,
// BadIndent(token as string, chars indented relative to root key)
BadIndent(String, usize),
// Relays error back to a function which knowns the token.
// InnerBadIndent(chars indented relative to root key)
InnerBadIndent(usize),
ColonBeforeKey,
// FirstTokenMustBeKey(token as string)
FirstTokenMustBeKey(String),
// IncompleteCommentOrkey(token as string)
IncompleteCommentOrKey(String),
// IncompleteLine(token as string)
IncompleteLine(String),
// NoColonAfterKey(token as string)
NoColonAfterKey(String),
// Occurs if the top key of the keytree is different from the search segment.
// BadFirstSegment(token, pos, key path)
BadFirstSegment(String, String, String),
// Occurs if a token other than the root token has zero indent.
// NonRootNoIndent(token, line)
NonRootNoIndent(String, Line),
NoSpaceAfterKey,
NoTokens,
EmptyPath,
}
// Equal if they are the same kind.
impl PartialEq for ErrorKind {
fn eq(&self, other: &Self) -> bool {
mem::discriminant(self) == mem::discriminant(other)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self.0 {
ErrorKind::EKeyFKeyValue => None,
ErrorKind::EKeyValueFKey => None,
ErrorKind::EUniqueKeyValueFMany => None,
ErrorKind::EUniqueTokenFMany => None,
ErrorKind::FailedFromStr(_, _, _) => None,
ErrorKind::NoChildWithSegment(_, _, _) => None,
ErrorKind::EmptyString => None,
ErrorKind::BadIndent(_, _) => None,
ErrorKind::InnerBadIndent(_) => None,
ErrorKind::ColonBeforeKey => None,
ErrorKind::FirstTokenMustBeKey(_) => None,
ErrorKind::IncompleteCommentOrKey(_) => None,
ErrorKind::IncompleteLine(_) => None,
ErrorKind::NoColonAfterKey(_) => None,
ErrorKind::BadFirstSegment(_, _, _) => None,
ErrorKind::NonRootNoIndent(_, _) => None,
ErrorKind::NoSpaceAfterKey => None,
ErrorKind::NoTokens => None,
ErrorKind::EmptyPath => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &*self.0 {
ErrorKind::EKeyFKeyValue => {
write!(f, "{}", "expected_key_found_key_value")
},
ErrorKind::EKeyValueFKey => {
write!(f, "{}", "expected_key_value_found_key")
},
ErrorKind::EUniqueKeyValueFMany => {
write!(f, "{}", "expected_unique_key_value_found_many")
},
ErrorKind::EUniqueTokenFMany => {
write!(f, "{}", "expected_unique_token_found_many")
},
ErrorKind::FailedFromStr(token, pos, into_type) => {
write!(
f,
"Error: Couldn't read string into type {} at {}: \"{}\".",
into_type,
pos,
token,
)
},
ErrorKind::NoChildWithSegment(token, line, child) => {
write!(
f,
"Error: Tried to follow child segment \"{}\" but found line {}: \"{}\".",
child,
line,
token,
)
},
ErrorKind::BadFirstSegment(token, line, path) => {
write!(f,
"Err: Tried to follow segment \"{}\" but found line {}: \"{}\".",
path,
line,
token,
)
},
ErrorKind::BadIndent(token, indent) => {
write!(f,
"Indent of \"{}\" relative to root key is {} but must be multiple of 4.",
token,
indent
)
},
// ErrorKind::Bincode(ref err) => Some(err),
ErrorKind::EmptyString => {
write!(f, "{}", "String is empty.")
},
ErrorKind::InnerBadIndent(_) => {
write!(f, "bug")
},
ErrorKind::ColonBeforeKey => {
write!(f, "{}", "colon before key")
},
ErrorKind::FirstTokenMustBeKey(token) => {
write!(f, "Expected first token to be key but was \"{}\"", token)
},
ErrorKind::IncompleteCommentOrKey(token) => {
write!(f, "\"{}\" is an incomplete comment or key", token)
},
ErrorKind::IncompleteLine(token) => {
write!(f,
"Line \"{}\" is incomplete.",
token,
)
},
ErrorKind::NoColonAfterKey(token) => {
write!(f, "Expected a colon after key \"{}\"", token)
},
ErrorKind::NonRootNoIndent(token, line) => {
write!(
f,
"\"{}\" at line {} has zero indent.",
token,
line,
)
},
ErrorKind::NoSpaceAfterKey => {
write!(f, "{}", "no space after key")
},
ErrorKind::NoTokens => {
write!(f, "{}", "no tokens")
},
ErrorKind::EmptyPath => {
write!(f, "{}", "empty path")
},
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{KeyTree, KeyTreeRef};
use std::convert::TryInto;
#[test]
fn is_empty() {
assert_eq!(
KeyTree::parse("")
.expect_err("")
.kind(),
ErrorKind::EmptyString,
);
assert_eq!(
KeyTree::parse("")
.expect_err("")
.to_string(),
"String is empty."
);
}
#[test]
fn first_token_is_kv() {
assert_eq!(
KeyTree::parse("key1: value")
.expect_err("")
.to_string(),
"Expected first token to be key but was \"key1: value\"",
)
}
#[test]
fn colon_before_key() {
assert_eq!(
KeyTree::parse("key1:\n :key2")
.expect_err("")
.kind(),
ErrorKind::ColonBeforeKey,
)
}
#[test]
fn incomplete_comment_or_key() {
assert_eq!(
KeyTree::parse("key:\n / text")
.expect_err("")
.to_string(),
"\"/\" is an incomplete comment or key",
)
}
#[test]
fn line_finishes_in_key() {
assert_eq!(
KeyTree::parse("key\n")
.expect_err("")
.to_string(),
"Line \"key\" is incomplete.",
)
}
#[test]
fn line_finishes_after_slash() {
assert_eq!(
KeyTree::parse("key:\n/")
.expect_err("")
.to_string(),
"\"/\" is an incomplete comment or key",
)
}
#[test]
fn no_colon_after_key() {
assert_eq!(
KeyTree::parse("key1:\n key2 value")
.expect_err("")
.to_string(),
"Expected a colon after key \"key2\"",
)
}
#[test]
fn no_space_after_key() {
assert_eq!(
KeyTree::parse("key1:\n key2:value")
.expect_err("")
.kind(),
ErrorKind::NoSpaceAfterKey,
)
}
#[derive(Debug)]
struct TestU32(u32);
impl<'a> TryInto<TestU32> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<TestU32, Error> {
Ok(TestU32(self.at("key::val")?))
}
}
#[test]
fn expected_u32() {
let kt = KeyTree::parse("key:\n val: abc").unwrap();
let t: Result<TestU32, Error> = kt.to_ref().try_into();
if let Err(err) = t {
assert_eq!(
err.kind(),
ErrorKind::FailedFromStr(String::new(), String::new(), String::new()),
)
} else {
assert!(false)
}
}
#[test]
fn expected_kv() {
let kt = KeyTree::parse("key:\n val:").unwrap();
let t: Result<TestU32, Error> = kt.to_ref().try_into();
if let Err(err) = t {
assert_eq!(
err.kind(),
ErrorKind::EKeyValueFKey
)
} else {
assert!(false)
};
}
#[test]
fn bad_indent() {
assert_eq!(
KeyTree::parse("key:\n key1: val") // 5 spaces
.expect_err("")
.to_string(),
"Indent of \"key1: val\" relative to root key is 5 but must be multiple of 4.",
)
}
// Try to properly handle hard to debug errors.
// This fails with:
// "thread 'test::same_indent' panicked at 'attempt to subtract with overflow', src/parser.rs:132:53"
//
# [test]
fn same_indent() {
assert_eq!(
KeyTree::parse("key1:\nkey2: val1")
.expect_err("")
.to_string(),
"\"key2: val1\" at line 2 has zero indent.",
)
}
}
// Keeps track of the last token at each indent level. These parents
// can be thought of as 'in scope'.
#[derive(Debug)]
pub (crate) struct Parents(pub Vec<usize>);
impl Parents {
pub fn new() -> Self {
Parents(Vec::new())
}
pub fn parent(&self, indent: usize) -> usize {
dbg!(&self.0);
self.0[indent]
}
pub fn truncate(&mut self, len: usize) {
self.0.truncate(len)
}
pub fn push(&mut self, token_i: usize) {
self.0.push(token_i);
}
}
// Same-Name-Siblings. Keeps track of siblings with the same keys.
#[derive(Debug)]
pub (crate) struct SNSibs(pub Vec<usize>);
impl SNSibs {
pub fn new() -> Self {
SNSibs(Vec::new())
}
pub fn sibling(&self, indent: usize) -> Option<usize> {
match self.0.len() > indent {
true => Some(self.0[indent]),
false => None,
}
}
pub fn truncate(&mut self, len: usize) {
self.0.truncate(len)
}
pub fn push(&mut self, token_ix: usize) {
self.0.push(token_ix);
}
}
use std::convert::TryInto;
use keytree::{KeyTree, KeyTreeRef};
use keytree::Error;
static HOBBITS: &'static str = r#"hobbit:
name: Frodo Baggins
age: 60
friends:
hobbit:
name: Bilbo Baggins
age: 111
hobbit:
name: Samwise Gamgee
age: 38
nick: Sam"#;
#[derive(Debug)]
struct Hobbit {
name: String,
age: u32,
friends: Vec<Hobbit>,
nick: Option<String>,
}
impl<'a> TryInto<Hobbit> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<Hobbit, Error> {
Ok(
Hobbit {
name: self.at("hobbit::name")?,
age: self.at("hobbit::age")?,
friends: self.vec("hobbit::friends::hobbit")?,
nick: self.op("hobbit::nick")?,
}
)
}
}
fn main() {
let kt = KeyTree::parse(HOBBITS).unwrap();
let hobbit: Hobbit = kt.to_ref().try_into().unwrap();
&dbg!(&hobbit);
}
[package]
name = "hobbit"
version = "0.1.0"
authors = ["Eric Findlay <e.findlay@protonmail.ch>"]
workspace = "../../"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
keytree = { path = "../.." }
# KeyTree
`KeyTree` is an elegant markup language designed to convert human readable information into Rust
data-structures. It is easy to implement for one's own types. Its main use is
for configuration files. The format looks like
```text
hobbit:
name: Frodo Baggins
age: 60
friends:
hobbit:
name: Bilbo Baggins
age: 111
hobbit:
name: Samwise Gamgee
age: 38
nick: Sam
```
so data can be recursive. Also, it is easy to refer to a set of data using a path such as
`hobbit::friends::hobbit` refers to a collection of two hobbits.
## Example
`Into` from `KeyTree` into Rust types is automatically implemented for `Vec<T>`, `Option<T>`
and basic Rust types. `KeyTree` text can be automatically converted to these data types, making
use of type inference. The `at()` function returns an iterator over `KeyTree` types that can be
used to implement `Into` for your own types. The following example should cover 90 percent of
use cases,
```rust
use core::convert::TryInto;
use keytree::{
error::Err,
KeyTree,
parser::KeyTreeBuilder,
};
#[derive(Debug)]
struct Hobbit {
name: String,
age: u32,
friends: Vec<Hobbit>,
nick: Option<String>,
}
impl<'a> TryInto<Hobbit> for KeyTree<'a> {
type Error = KeyErr;
fn try_into(self) -> Result<Hobbit, Self::Error> {
Ok(
Hobbit {
name: self.at("hobbit::name")?,
age: self.at("hobbit::age")?,
friends: self.vec("hobbit::friends::hobbit")?,
nick: self.op("hobbit::nick")?,
}
)
}
}
fn main() {
let core = KeyTreeBuilder::parse("hobbit.keytree").unwrap();
let hobbit: Hobbit = KeyTree::from_core(&core).try_into().unwrap();
dbg!(&hobbit);
}
```
## Details
We'll have a look in detail at what is happening in the example on the line
```text
friends: self.at("hobbit::friends::hobbit"),
```
In the `Into` trait impl, we want Bilbo Baggins and Samwise Gamgee to be Frodo's
friends. We specify their location in the `KeyTree` string using a path
`"hobbit::friends::hobbit"` which refers to two branches in the tree (two
hobbits). The `at()` function, unlike the the `op()` function, requires that the
branches exist. Rust infers that they need to be converted into the type `Vec<Hobbit>` as
specified in the `Hobbit` struct. The `Vec<T>` impl of `Into` is supplied by `KeyTree`. In fact
the `at()` function supplies an iterator over the Hobbits, which the `Vec<T>` impl uses.
## Data Format Rules
- Indentation has meaning and is 4 spaces, relative to the top key. Since indenting is
relative to the top key, then you can neatly align strings embedded in code.
- Each line can be empty, have whitespace only, be a comment, be a key, or be a key/value
pair.
- There are keys and values. Key/Value pairs look like
```text
name: Frodo
```
are used for `struct` fields and `enum` variants.
Keys refer to child keys or child key/value pairs indented on lines under it, for example
```text
hobbit:
name: Frodo
```
hobbit refers to the name of the struct or enum. In this way, the data maps simply to Rust
data-structures.
- If a key has many children with the same key, it forms a collection, for example
```test
hobbit:
name: Frodo
name: Bilbo
```
is a collection of hobbits.
- Keys must not include but must be followed by a colon `:`.
- Values are all characters between the combination of ':' and whitespace and the end of the
line. The value is trimmed of whitespace at both ends.
- Comments require `//` at the start of the line. For example
```text
// comment
hobbit:
name: Frodo
```
[package]
name = "keytree"
version = "0.1.0"
authors = ["Eric Findlay <e.findlay@protonmail.ch>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[workspace]
members = [ "examples/hobbit", "examples/embedded" ]
[lib]
doctest = false