# 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.
## 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, Small)]
struct Hobbit {
name: String,
age: u32,
friends: Vec<Hobbit>,
bicycle: Option<String>,
}
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);
}
```
## `FromSmall` Trait
Types that implement the `FromSmall` trait can be constructed from a `Small`-formatted string.
Required function:
```rust
from_small(slice: &Small) -> Result<Self, SmallError>
```
The `from_small()` function describes how to create a data-structure from the parts of
`Small`. See example 1 for canonical usage.
```rust
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.
```rust
from_str(s: &str) -> Result<Self, SmallError>
```
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<Self, SmallError> {
let token = s.unique_value()?;
token
.value()?
.parse::<u32>()
.map_err(|_| SmallError::ParseValue(token, "u32"))
}
}
```