KD6IJRPTTSGM3U6FQKUPVXUJ3SAF5BEU2UD4M6EASFO56WLHKSTAC
# 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
```
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
//! ```