KD6IJRPTTSGM3U6FQKUPVXUJ3SAF5BEU2UD4M6EASFO56WLHKSTAC # 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```
Token::KeyToken(kt) => kt.to_string(max_key_len),Token::KeyValToken(kvt) => kvt.to_string(max_key_len),Token::Comment(c) => c.to_string(max_key_len),
Token::KeyToken(kt) => kt.to_str(),Token::KeyValToken(kvt) => kvt.to_str(val_indent),Token::Comment(c) => c.to_str(),
// Takes number of chars and returns minimum indent that is greater than the number of chars.pub fn chars_to_indent(chars: usize) -> usize {// 0 -> 0// 1 -> 1// 2 -> 1// 3 -> 1// 4 -> 1// 5 -> 2(chars + 3) / 4}#[test]fn test_chars_to_indent() {assert_eq!(chars_to_indent(0), 0);assert_eq!(chars_to_indent(1), 1);assert_eq!(chars_to_indent(4), 1);assert_eq!(chars_to_indent(5), 2);}
for tok in &self.tokens {s.push_str(&tok.to_string(self.max_key_len));
for token in &self.tokens {match token {Token::KeyValToken(kvt) => s.push_str(&kvt.to_str(self.val_indent)),Token::KeyToken(kt) => s.push_str(&kt.to_str()),Token::Comment(com) => s.push_str(&com.to_str()),}
/// Append string `t` to end of `s` at indent `indent`. Panic if/// strings overlap.fn append_at_indent(indent: usize, key: &str, val: &str) -> String {let mut s = String::from(key);let padding = (indent * 4) - s.chars().count() - 1;for _ in 0..=padding { s.push(' ') };s.push_str(&val);s
#[test]fn test_2() {let mut kt_string = KeyTreeString::new();kt_string.push_keyvalue(0, "key", "value");assert_eq!(kt_string.to_string(),"key: value",)
//! # KeyTree//!//! `KeyTree` is a text format 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 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.value("hobbit::name")?,//! age: self.value("hobbit::age")?,//! friends: self.vec_at("hobbit::friends::hobbit")?,//! nick: self.opt_value("hobbit::nick")?,//! }//! )//! }//! }//!//! fn main() {//! let kt = KeyTree::parse(HOBBITS).unwrap();//! let hobbit: Hobbit = kt.to_ref().try_into().unwrap();//!//! &dbg!(&hobbit);//! }//! ```//!//! In the `TryInto` implementation that deserializes the keytree string into a data-structure, the//! line//! ```//! name: self.value("hobbit::name")?,//! ```//! takes the value on the line specified by `hobbit:name` (`Frodo Baggins`) and then uses the//! `FromStr` implement to convert it into the receiver type of the `name` field which is type//! `String`.//!//! The line//! ```//! friends: self.vec_at("hobbit::friends::hobbit")?,//! ```//! takes the collection of lines//! ```text//! hobbit://! name: Bilbo Baggins//! age: 111//! hobbit://! name: Samwise Gamgee//! age: 38//! nick: Sam//! ```//! and then uses the `TryInto` implementation of the items in the receiving type `Hobbit` to map//! itself into a `Vec` of `Hobbit`s.//!//! ## 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//! ```