Toml Example
A lib help generate toml example
Introduction
This crate provides the TomlExample trait and an accompanying derive macro.
Deriving TomlExample on a struct will provide to_example function help generate toml example file base documentation
- support
#[serde(default)],#[serde(default = "function_name")]attributes on both structs and struct fields (serdefeature, opt-in) - support
#[serde(rename)],#[serde(rename_all = "renaming rules")], the renaming rules can belowercase,UPPERCASE,PascalCase,camelCase,snake_case,SCREAMING_SNAKE_CASE,kebab-case,SCREAMING-KEBAB-CASE - provide
#[toml_example(default)],#[toml_example(default = 0)],#[toml_example(default = "default_string")]attributes on struct fields #[toml_example(default)]is also supported as an outer attribute for structs- The order matter of attribute macro, if
#[serde(default = ..]and#[toml_example(default = ..)]existing at the same time with different value
Quick Example
use TomlExample;
use Deserialize;
/// Config is to arrange something or change the controls on a computer or other device
/// so that it can be used in a particular way
to_toml_example; // write example to a file
let example = toml_example;
Toml example base on the docstring of each field
# Config is to arrange something or change the controls on a computer or other device
# so that it can be used in a particular way
# Config.a should be a number
= 0
# Config.b should be a string
= ""
# Optional Config.c is a number
# c = 0
# Config.d is a list of number
# d = [ 0, ]
# Config.e should be a number
= 7
# Config.f should be a string
= "seven"
# Config.g should be a number
= 7
# Config.h should be a string
= "seven"
The fields of a struct can inherit their defaults from the parent struct when the
#[toml_example(default)], #[serde(default)] or #[serde(default = "default_fn")]
attribute is set as an outer attribute of the parent struct:
use Serialize;
use TomlExample;
assert_eq!;
Nesting Struct
A nesting struct wrap with Option<T>, Vec<T>, HashMap<String, T>, BTreeMap<String, T> are handled.
Please add #[toml_example(nesting)], or #[toml_example(nesting = prefix)] on the field.
#[toml_example(nesting)]
/// Service with specific port
Node::toml_example() will be following string.
# Services are running in the node
# Service with specific port
[]
# port should be a number
= 80
Flattening
Flattening means treating the fields of a nested struct as if they were defined directly in the wrapping struct.
assert_eq!;
This works with maps too!
let example = toml_example;
assert!;
println!;
[]
= false
Enum Field
You can also use field less enums, but you have to annotate them with #[toml_example(enum)] or
#[toml_example(is_enum)] if you mind the keyword highlight you likely get when writing "enum".
When annotating a field with #[toml_example(default)] it will use the Debug implementation.
However for non-TOML data types like enums, this does not work as the value needs to be treated as a string in TOML.
The #[toml_example(enum)] attribute just adds the needed quotes around the Debug implementation
and can be omitted if a custom Debug already includes those.
use TomlExample;
assert_eq!
Ignoring Comments / Developer Only Comment / Help Text
We have several ways to handle a mix of toml examples for the end user, and let some documents show for the developer only. Although the crate does not forbid using these ways at the same time, it is good to just use one and keep your code base simple. You can check out the issue #75 to learn more about these.
Using Rust native features
We can write some doc with a feature, as follows.
#[cfg_attr(feature = "dev-docs", doc = "This paragraph only appears when dev-docs feature is enabled.")].
Then, the doc can be shown only when the feature enabled.
# Multiple features
cargo doc --features "dev-docs"
# All features
cargo doc --all-features
Using #[toml_example(doc_skip_prefix = "...")]
Normal Rust comments (the ones beginning with // or those: /**/) are not included in the TOML example.
Rust doc comments can be excluded from the TOML example by adding a #[toml_example(doc_skip_prefix = "prefix")] attribute and then prefixing every comment line that should not be included in the
TOML example with the specified prefix:
use TomlExample;
// We have to use double backslash here, else it will fuck up the AST
assert_eq!
Using #[toml_example(help = "...")]
You can use #[toml_example(help = "...")] to provide custom help text for the TOML example.
When help is set, the doc string is ignored and the help text is used as the TOML comment instead.
This works on both struct fields and structs themselves, and can be combined with other attributes.
use TomlExample;
# The port number to listen on
= 8080
More
If you want an optional field become a required field in example,
place the #[toml_example(require)] on the field.
If you want to skip some field you can use #[toml_example(skip)],
the #[serde(skip)], #[serde(skip_deserializing)] also works.
use TomlExample;
# Config.a is an optional number
= 0
# Config.b is an optional string
= ""
= "third"
Why TOML is good for program configuration file
There are several common config file solutions: INI, JSON, YAML, TOML.
- INI is legacy and no nesting support, and the comment char is not designed at first, so some parser will use
;or#. - JSON does not support documentation. It is efficient for data exchange, but configuration files should include documentation, so it is not good for this use case.
- YAML is too greedy on covering derserialize issue, so it is too complex for a configuration and vulnerable, for example:
yambiguously stand for "y" or true- CVE-2019-11253
- TOML is good for now, and easy to write and read for human, that is a real need for configuration file.