# SML [![Build Status](https://api.travis-ci.org/ericfindlay/SML.svg)](https://travis-ci.org/ericfindlay/SML) [![](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT) `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 ```text 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 ```text name: "Frodo" ``` are used for `struct` fields and `enum` variants. Keys only ```text hobbit: name: "Frodo" ``` indicate a complete `struct` or `enum`. In this way, the data clearly indicates the mapping to Rust data structures. 4. Separation of lines has meaning. 5. Keys must not include but must be followed by a colon `:`. 6. Double quotes in values must be escaped using `\"`. 7. Everything after the second double quote is ignored. 8. Empty lines or lines with whitespace only are ignored. 9. Comments require `//` at the start of the line for example ```text // comment hobbit: name: "Frodo" ``` ## Example. From `Small`-formatted string to your data-structure. This examples should cover 90 percent of use cases. ```rust use sml::{Small, FromSmall, SmallError}; #[derive(Debug)] struct Hobbit { name: String, age: u32, friends: Vec, bicycle: Option, } impl FromSmall for Hobbit { fn from_small(s: &Small) -> Result { Ok(Hobbit { name: String::path(&s, "hobbit::name")?, age: u32::path(&s, "hobbit::age")?, friends: Vec::::path(&s, "hobbit::friends::hobbit")?, bicycle: Option::::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: ```rust from_small(slice: &Small) -> Result ``` The `from_small()` function describes how to create a data-structure from the parts of `Small`. ```rust path(small: &Small, key_path: &str) -> Result ``` Reduces `Small` to the `key_path` and then uses the `FromSmall` trait to convert to the receiver type. ```rust from_str(s: &str) -> Result ``` Top level function that convert a `Small`-formatted string into the receiver. ```rust 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, ```text hobbit: name: "Bilbo Baggins" age: "176" hobbit: name: "Samwise Gamgee" age: "66" ``` is a collection of two elements ```text hobbit: name: "Bilbo Baggins" age: "176" ``` and ```text 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`. ```rust impl FromSmall for u32 { fn from_small(s: &Small) -> Result { let token = s.unique_value()?; token .value()? .parse::() .map_err(|_| SmallError::ParseValue(token, "u32")) } } ```