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 keyIK, // In key.RAK, // The character right after the key.AK, // After key.IV, // In value.CM, // In comment}// Chars receivedenum 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 parentparents: 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 eachfn 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.mdpub 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 nowline: Line, // used for debugging},KeyValue {key: &'a str,value: &'a str,next: Option<usize>, // refers to Vec index for nowline: 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>whereKeyTreeRef<'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 uniquematch 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>>whereKeyTreeRef<'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 uniquematch kt.top_token().next() {None => {// re-wrapmatch 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>>whereKeyTreeRef<'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 Bagginsage: 60friends:hobbit:name: Bilbo Bagginsage: 111hobbit:name: Samwise Gamgeeage: 38nick: 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 BadIndentpub 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 Bagginsage: 60friends:hobbit:name: Bilbo Bagginsage: 111hobbit:name: Samwise Gamgeeage: 38nick: 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 Rustdata-structures. It is easy to implement for one's own types. Its main use isfor configuration files. The format looks like```texthobbit:name: Frodo Bagginsage: 60friends:hobbit:name: Bilbo Bagginsage: 111hobbit:name: Samwise Gamgeeage: 38nick: 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, makinguse of type inference. The `at()` function returns an iterator over `KeyTree` types that can beused to implement `Into` for your own types. The following example should cover 90 percent ofuse cases,```rustuse 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);}```## DetailsWe'll have a look in detail at what is happening in the example on the line```textfriends: self.at("hobbit::friends::hobbit"),```In the `Into` trait impl, we want Bilbo Baggins and Samwise Gamgee to be Frodo'sfriends. We specify their location in the `KeyTree` string using a path`"hobbit::friends::hobbit"` which refers to two branches in the tree (twohobbits). The `at()` function, unlike the the `op()` function, requires that thebranches exist. Rust infers that they need to be converted into the type `Vec<Hobbit>` asspecified in the `Hobbit` struct. The `Vec<T>` impl of `Into` is supplied by `KeyTree`. In factthe `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 isrelative 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/valuepair.- There are keys and values. Key/Value pairs look like```textname: 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```texthobbit:name: Frodo```hobbit refers to the name of the struct or enum. In this way, the data maps simply to Rustdata-structures.- If a key has many children with the same key, it forms a collection, for example```testhobbit:name: Frodoname: 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 theline. The value is trimmed of whitespace at both ends.- Comments require `//` at the start of the line. For example```text// commenthobbit: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