63 breaking releases
0.133.0 | Mar 19, 2024 |
---|---|
0.129.0 | Jul 18, 2022 |
0.111.0 | Mar 11, 2022 |
0.95.0 | Dec 25, 2021 |
0.45.0 | Jul 7, 2021 |
#65 in Configuration
Used in watchlog
57KB
1.5K
SLoC
Simple Config
A config language for humans that is not self-describing.
Simple Config isn't specific to Rust or serde but the only known implementation uses both.
Why Use Simple Config?
Simple config is based on the concept that you don't need to be able to form the schema from the config file. This is already common but in a more adhoc form, for example you may use a JSON string to hold a date such as "1990-02-14"
. Simple Config takes advantage of this to provide richer types and less escaping. For example a multi-line string requires no additional markup, the schema says it is a string so it won't try to parse it as a dict. Similarly you won't have type errors because false
or no
got parsed as a bool, and you don't have to worry about quoting strings that start with numbers.
Why Not Use Simple Config?
- Can not be parsed without the schema. (Note: Unknown items can be skipped, but it can't be semantically parsed.)
- No support for code reuse. Simple Config is best for relatively simple config files where reuse is not required.
- No current support for serialization yet.
Example
This is a small example that shows both the schema (as Rust code) and the config (as a string literal). For a full explanation see below.
#[derive(Debug,PartialEq,serde::Deserialize)]
enum Country {
CA,
US,
NO,
}
#[derive(Debug,PartialEq,serde::Deserialize)]
#[serde(rename_all="kebab-case")]
enum Contact {
Email(String),
Matrix(String),
Phone{
number: u64,
#[serde(default)]
extension: Option<u64>,
},
}
#[derive(Debug,PartialEq,serde::Deserialize)]
#[serde(rename_all="kebab-case")]
struct Contestant {
contact: Vec<Contact>,
country: Country,
#[serde(with = "humantime_serde")]
personal_best: std::time::Duration,
#[serde(with = "humantime_serde")]
last_seen: std::time::SystemTime,
}
let t: std::collections::BTreeMap<String, Contestant> = simple_config::from_bytes("
kevincox:
contact:
:
email: kevincox@kevincox.ca
:
phone:
number: 18005551234
country: CA
personal-best: 1h 13min
last-seen: 2021-06-08 00:00:00Z
sarah:
contact:
:
matrix: @me:matrix.example
country: NO
personal-best: 73min
last-seen: 2019-06-30 07:24:30Z
").unwrap();
assert_eq!(t, std::array::IntoIter::new([
("kevincox".to_owned(), Contestant {
contact: vec![
Contact::Email("kevincox@kevincox.ca".into()),
Contact::Phone{number: 18005551234, extension: None},
],
country: Country::CA,
personal_best: std::time::Duration::from_secs(1*3600 + 13*60),
last_seen: std::time::UNIX_EPOCH + std::time::Duration::from_secs(1623110400),
}),
("sarah".to_owned(), Contestant {
contact: vec![
Contact::Matrix("@me:matrix.example".into()),
],
country: Country::NO,
personal_best: std::time::Duration::from_secs(73*60),
last_seen: std::time::UNIX_EPOCH + std::time::Duration::from_secs(1561879470),
}),
]).collect());
For more examples see the examples directory. Note that some of the examples in this directory re-implementations of existing data structures and may contain hacks to work well with the previous config language, leading to suboptimal presentation in Simple Config.
Reference
Dictionaries
key: value
Dictionaries are a mapping from a key to a value. The key is always a single-line value that doesn't contain a colon followed by a colon, however the exact parsing is dependent on the type.
# The type of the key depends on the schema, but these examples can be parsed as the specified type.
true: boolean key
123: integer key
hello: string key
Values are arbitrary types. There are two ways to specify a value. How these values are parsed depends on the type.
key: inline value
key:
multi
line
value
For full reference on parsing see the docs for the type of the value.
Nesting
Dictionaries can be nested, when nested the value must be specified as a mutli-line value.
vehicle:
type: Car
engine:
capacity_cc: 200
mass_g: 60k
wheels:
diameter_m: 550m
Lists
Lists contain multiple values. Depending on the schema the order may or may not be important.
strings:
string one
string two
string three
numbers:
1
1k
1M
By default list items are a single line long. To put a multi-line item in a list you must use the :
character to open a new level of indent. Single and multi-line items can be mixed. Note that Dictionaries must be multi-line.
strings:
single line string
:
multi
line
string
another single line string
dicts:
:
species: Cat
cuteness: 0.99
:
species: Dog
cuteness: 0.8
Strings
Strings can be single-line or multi-line. Currently no escaping is allowed or necessary, strings end at a newline or comment (#
) whichever comes first. However single-line strings starting with a double quote "
are reserved for future escaping capabilities. Mutli-line strings have the leading indentation stripped. Note that there are no special characters in a multi-line string, including comments. If you want to put a comment in a multi-line string you will need to use the destination comment format.
inline: a string # This comment is not part of the string.
out-of-line:
a string
multi-line: # This comment is not part of the string.
this
is
a
string
no-comments:
# This comment is a part of the string.
Warning: There is currently no way to specify a string where the first line is indented.
# WARNING: This example is invalid.
doesnt-work:
indented first line
non-indented line
Numbers
Numbers can be specified as you would expect. Additionally many suffixes are supported for scaling the numbers. It is an error to have an integer that is out-of-range for the associated type. Floating point values will be the nearest representable value.
simple-int: 123
simple-float: 3.14
base2: 0b01001011
base8: 0o664
base16: 0xCC
# Currently arbitrary exponents are only supported for base10.
exponents:
6.022e1023 # 6.022 ⨉ 10^1023
2.5e-11 # 2.5 ⨉ 10^-11
si-suffixes:
1E
1P
1T
1G
1M
1k # K is also accepted for consistency.
1m
1u
1µ
1n
1p
1f
1a
iec-sufixes:
1Ei
1Pi
1Ti
1Gi
1Mi
1ki # Ki is also accepted for consistency.
1mi
1ui
1µi
1ni
1pi
1fi
1ai
Booleans
Booleans are specified as true
or false
.
Bytes
Bytes are currently unsupported. https://gitlab.com/kevincox/simple-config-rs/-/issues/1
Optionals
An optional value can be specified as an empty value value.
Note: It is currently impossible to specify an empty string for Option<String>
, it is always assumed to mean None.
maybe_none:
maybe_some: 17
Comments
Comments start with a #
and extend to the end of the line. Comments can be placed in most places. However note that comments can't be placed in multi-line strings (they are considered part of the string).
# comment
a-number: # comment
5
another-number: 5 # comment
a-string: # comment
hi
multi-line-string: # comment
hi # NOT COMMENT
# NOT COMMENT
# comment
list: # comment
1 # comment
# comment
2
# comment
Dependencies
~1–1.8MB
~34K SLoC