#markup-language #schema #human-readable #loading #format #baggins

sml

Simple markup language optimized for loading config files and schemas

44 releases

0.1.46 Apr 17, 2019
0.1.45 Apr 15, 2019
0.1.6 Mar 31, 2019

#1138 in Encoding

Download history 75/week @ 2024-03-31

92 downloads per month

MIT license

43KB
735 lines

SML

Build Status

SML is a simple markup language designed to convert human readable information into Rust types with a very specific purpose of loading config files and schemas.

The format looks like

hobbit:
    name:           Frodo Baggins
    age:            98
    friends:
        hobbit:
            name:   Bilbo Baggins
            age:    176
        hobbit:
            name:   Samwise Gamgee
            age:    66

Data Format Rules

  1. Indentation has meaning and is 4 spaces, relative to the top key. If indenting is relative to the top key, then you can neatly align strings embedded in code.

  2. All values must be double quoted.

  3. Key/value combinations are used for fields

name: "Frodo"

are used for struct fields and enum variants. Keys only

hobbit:
    name: "Frodo"

indicate a complete struct or enum. In this way, the data clearly indicates the mapping to Rust data structures.

  1. Separation of lines has meaning.

  2. Keys must not include but must be followed by a colon :.

  3. Double quotes in values must be escaped using \".

  4. Everything after the second double quote is ignored.

  5. Empty lines or lines with whitespace only are ignored.

  6. Comments require // at the start of the line for example

// comment
hobbit:
    name: "Frodo"

Example. From Small-formatted string to your data-structure.

This examples should cover 90 percent of use cases.

use sml::{Small, FromSmall, SmallError};

#[derive(Debug)]
struct Hobbit {
    name:    String,
    age:     u32,
    friends: Vec<Hobbit>,
    bicycle: Option<String>,
}

impl FromSmall for Hobbit {
    fn from_small(s: &Small) -> Result<Self, SmallError> {
        Ok(Hobbit {
            name:    String::path(&s, "hobbit::name")?,
            age:     u32::path(&s, "hobbit::age")?,
            friends: Vec::<Hobbit>::path(&s, "hobbit::friends::hobbit")?,
            bicycle: Option::<String>::path(&s, "hobbit::bicycle")?,
        })
    }
}

fn main() {
    let s = r#"
        hobbit:
            name:         "Frodo Baggins"
            age:          "98"
            friends:
                hobbit:
                    name: "Bilbo Baggins"
                    age:  "176"
                hobbit:
                    name: "Samwise Gamgee"
                    age:  "66""#;
    
    let frodo = Hobbit::from_str_debug(s);
    println!("name: {}", frodo.name);
}

FromSmall Trait

Types that implement the FromSmall trait can be constructed from a Small-formatted string.

Required function:

from_small(slice: &Small) -> Result<Self, SmallError>

The from_small() function describes how to create a data-structure from the parts of Small.

path(small: &Small, key_path: &str) -> Result<Self, SmallError>

Reduces Small to the key_path and then uses the FromSmall trait to convert to the receiver type.

from_str(s: &str) -> Result<Self, SmallError>

Top level function that convert a Small-formatted string into the receiver.

from_str_debug(s: &str) -> Self

Top level function that converts a Small-formatted string into the receiver giving helpful error messages for debugging.

Implementation

A Small value may be a collection of Small values. For example,

hobbit:
    name: "Bilbo Baggins"
    age:  "176"
hobbit:
    name: "Samwise Gamgee"
    age:  "66"

is a collection of two elements

hobbit:
    name: "Bilbo Baggins"
    age:  "176"

and

hobbit:
    name: "Samwise Gamgee"
    age:  "66"

Often when implementing FromSmall we want to convert a Small object into a single value, so we need to check that Small has only one element The implementation to convert from Small to u32 gives indicates how to do this. unique_value() checks that there is only one element in Small and if so returns that one element and value() extracts that value as a String.

impl FromSmall for u32 {
    fn from_small(s: &Small) -> Result<Self, SmallError> {
        let token = s.unique_value()?;
        token
            .value()?
            .parse::<u32>()
            .map_err(|_| SmallError::ParseValue(token, "u32"))
    }
}

Dependencies

~0–7.5MB
~45K SLoC