First commit

[?]
Feb 21, 2021, 11:55 AM
6ZJX2OQV5MYICMONYVYP55DUKORJ3KPLIN7LHAYLXY7ZB7SZE7TAC

Dependencies

Change contents

  • file addition: src (dxwrx-rx-r)
    [1.0]
  • file addition: path.rs (-xw-xw-x--)
    [0.6]
    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))
    }
    }
  • file addition: parser.rs (-xw-xw-x--)
    [0.6]
    #![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)
    }
    }
    }
    }
  • file addition: lib.rs (-xw-xw-x--)
    [0.6]
    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,
    // );
    }
    }
  • file addition: into.rs (-xw-xw-x--)
    [0.6]
    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")
    }
    }
  • file addition: error.rs (-xw-xw-x--)
    [0.6]
    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.",
    )
    }
    }
  • file addition: builder.rs (-xw-xw-x--)
    [0.6]
    // 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);
    }
    }
  • file addition: examples (dxwrx-rx-r)
    [1.0]
  • file addition: hobbit (dxwrx-rx-r)
    [0.48825]
  • file addition: src (dxwrx-rx-r)
    [0.48835]
  • file addition: main.rs (-xw-xw-x--)
    [0.48842]
    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);
    }
  • file addition: Cargo.toml (-xw-x--x--)
    [0.48835]
    [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 = "../.." }
  • file addition: README.md (-xw-x--x--)
    [1.0]
    # 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
    ```
  • file addition: Cargo.toml (-xw-xw-x--)
    [1.0]
    [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