Improved header documentation.

[?]
Jul 17, 2021, 1:48 AM
KD6IJRPTTSGM3U6FQKUPVXUJ3SAF5BEU2UD4M6EASFO56WLHKSTAC

Dependencies

Change contents

  • file deletion: README.md (-xw-x--x--)
    [1.0][3.50189:50200](),[3.50200][3.50201:50201]()
    # 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
    ```
  • edit in src/serialize.rs at line 2
    [2.30]
    [2.30]
    use std::fmt::Display;
  • replacement in src/serialize.rs at line 15
    [2.210][2.210:270]()
    pub fn to_string(&self, max_key_len: usize) -> String {
    [2.210]
    [2.270]
    pub fn to_str(&self, val_indent: usize) -> String {
  • replacement in src/serialize.rs at line 17
    [2.291][2.291:479]()
    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),
    [2.291]
    [2.479]
    Token::KeyToken(kt) => kt.to_str(),
    Token::KeyValToken(kvt) => kvt.to_str(val_indent),
    Token::Comment(c) => c.to_str(),
  • replacement in src/serialize.rs at line 30
    [2.575][2.575:691]()
    pub fn to_string(&self, _max_key_len: usize) -> String {
    let mut s = indent_to(self.indent, &self.key);
    [2.575]
    [2.691]
    pub fn to_str(&self) -> String {
    let mut s = String::new();
    s.push_str(&padding(self.indent * 4));
    s.push_str(&self.key);
  • replacement in src/serialize.rs at line 46
    [2.831][2.831:946]()
    pub fn to_string(&self, max_key_len: usize) -> String {
    let mut s = indent_to(self.indent, &self.key);
    [2.831]
    [2.946]
    pub fn to_str(&self, val_indent: usize) -> String {
    let mut s = String::new();
    s.push_str(&padding(self.indent * 4));
    s.push_str(&self.key);
  • replacement in src/serialize.rs at line 51
    [2.972][2.972:1042]()
    append_at_indent(chars_to_indent(max_key_len), &s, &self.val)
    [2.972]
    [2.1042]
    s.push_str(&padding(val_indent - (self.key.len() + 2 + self.indent * 4)));
    s.push_str(&self.val);
    s
  • edit in src/serialize.rs at line 57
    [2.1051][2.1051:1506]()
    // 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);
    }
  • replacement in src/serialize.rs at line 63
    [2.1585][2.1585:1697]()
    pub fn to_string(&self, _max_key_len: usize) -> String {
    let mut s = indent_to(self.indent, "// ");
    [2.1585]
    [2.1697]
    pub fn to_str(&self) -> String {
    let mut s = String::new();
    s.push_str(&padding(self.indent * 4));
    s.push_str("// ");
  • replacement in src/serialize.rs at line 74
    [2.1802][2.1802:1826]()
    max_key_len: usize,
    [2.1802]
    [2.1826]
    /// The indentation in chars to align values.
    val_indent: usize,
  • replacement in src/serialize.rs at line 83
    [2.1933][2.1933:1961]()
    max_key_len: 0,
    [2.1933]
    [2.1961]
    val_indent: 0,
  • edit in src/serialize.rs at line 98
    [2.2320]
    [2.2320]
    pub fn push_opt_value<T: Display>(&mut self, indent: usize, key: &str, value: Option<T>) {
    match value {
    None => return,
    Some(v) => {
    self.push_value(indent, key, v.to_string().as_str())
    },
    }
    }
  • replacement in src/serialize.rs at line 108
    [2.2417][2.2417:2494]()
    pub fn push_keyvalue(&mut self, indent: usize, key: &str, value: &str) {
    [2.2417]
    [2.2494]
    pub fn push_value<T: Display>(&mut self, indent: usize, key: &str, value: T) {
  • replacement in src/serialize.rs at line 113
    [2.2629][2.2629:2671]()
    val: String::from(value),
    [2.2629]
    [2.2671]
    val: value.to_string(),
  • replacement in src/serialize.rs at line 117
    [2.2726][2.2726:2804]()
    // Colon and space
    [2.2726]
    [2.2804]
    // |
    // v
    // 012345
    // key:
  • replacement in src/serialize.rs at line 123
    [2.2866][2.2866:2946]()
    if key_len > self.max_key_len {
    self.max_key_len = key_len;
    [2.2866]
    [2.2946]
    // | |
    // v v
    // 0123456
    let val_indent = match key_len % 4 {
    0 => key_len,
    1 => key_len + 3,
    2 => key_len + 2,
    3 => key_len + 1,
    _ => unreachable!(),
  • edit in src/serialize.rs at line 134
    [2.2957]
    [2.2957]
    // val_indent (5)
    // |
    // v
    // 012345
    // key:
    //
    // (padding = val_indent - (key.len() + 2)
    if val_indent > self.val_indent {
    self.val_indent = val_indent;
    };
  • replacement in src/serialize.rs at line 167
    [2.3732][2.3732:3810]()
    self.push_keyvalue(kv.indent + indent, &kv.key, &kv.val);
    [2.3732]
    [2.3810]
    self.push_value(kv.indent + indent, &kv.key, &kv.val);
  • edit in src/serialize.rs at line 175
    [2.3986][2.3986:4262]()
    }
    #[test]
    fn test_keytree_string1() {
    let mut kts = KeyTreeString::new();
    kts.push_key(0, "key");
    kts.push_key(1, "a_key");
    kts.push_keyvalue(1, "b_key", "b_val");
    assert_eq!(
    kts.to_string(),
    "key:\n a_key:\n b_key: b_val",
    )
  • replacement in src/serialize.rs at line 180
    [2.4401][2.4401:4493]()
    for tok in &self.tokens {
    s.push_str(&tok.to_string(self.max_key_len));
    [2.4401]
    [2.4493]
    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()),
    }
  • replacement in src/serialize.rs at line 193
    [2.4582][2.4582:4691]()
    /// Append whitespace of length `n` to start of a string.
    fn indent_to(indent: usize, key: &str) -> String {
    [2.4582]
    [2.4691]
    fn padding(len: usize) -> String {
  • replacement in src/serialize.rs at line 195
    [2.4722][2.4722:4790]()
    for _ in 0..(indent * 4) { s.push(' ') };
    s.push_str(&key);
    [2.4722]
    [2.4790]
    for _ in 0..len {
    s.push(' ');
    }
  • replacement in src/serialize.rs at line 202
    [2.4807][2.4807:4829]()
    fn test_indent_to() {
    [2.4807]
    [2.4829]
    fn test_1() {
    let mut kt_string = KeyTreeString::new();
    kt_string.push_keyvalue(1, "key", "value");
  • replacement in src/serialize.rs at line 206
    [2.4845][2.4845:4893]()
    indent_to(1, "abc"),
    " abc",
    [2.4845]
    [2.4893]
    kt_string.to_string(),
    " key: value",
  • replacement in src/serialize.rs at line 211
    [2.4902][2.4902:5218]()
    /// 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
    [2.4902]
    [2.5218]
    #[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",
    )
  • replacement in src/serialize.rs at line 222
    [2.5229][2.5229:5258]()
    fn test_append_at_indent() {
    [2.5229]
    [2.5258]
    fn test_3() {
    let mut kt_string = KeyTreeString::new();
    kt_string.push_keyvalue(0, "ky", "value");
  • replacement in src/serialize.rs at line 226
    [2.5274][2.5274:5348]()
    append_at_indent(3, " abc", "def"),
    " abc def",
    [2.5274]
    [2.5348]
    kt_string.to_string(),
    "ky: value",
  • replacement in src/serialize.rs at line 230
    [2.5356][2.5356:5361]()
    [2.5356]
  • edit in src/lib.rs at line 1
    [3.18441]
    [3.18442]
    //! # 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
    //! ```
  • replacement in Cargo.toml at line 12
    [3.54215][3.54215:54268]()
    members = [ "examples/hobbit", "examples/embedded" ]
    [3.54215]
    [3.54268]
    members = [ "examples/hobbit" ]