# Simple Config [Docs](https://docs.rs/simple_config/latest/simple_config/) A config language for humans that is not self-describing. Simple Config isn't specific to Rust or [serde](https://serde.rs/) 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. ```rust #[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, }, } #[derive(Debug,PartialEq,serde::Deserialize)] #[serde(rename_all="kebab-case")] struct Contestant { contact: Vec, 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 = 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](examples/). 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 ```yaml 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. ```yaml # 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. ```yaml 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. ```yaml 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. ```yaml 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. ```yaml 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. ```yaml 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. ```yaml # 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. ```yaml 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. ### Optionals An optional value can be specified as an empty value value. Note: It is currently impossible to specify an empty string for `Option`, it is always assumed to mean None. ```yaml 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). ```yaml # 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 ```