Skip to main content

Crate toml_example

Crate toml_example 

Source
Expand description

This crate provides the TomlExample trait and an accompanying derive macro.

Deriving TomlExample on a struct will generate functions toml_example(), to_toml_example(file_name) for generating toml example content.

The following code shows how toml-example can be used.

use toml_example::TomlExample;

/// 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
#[derive(TomlExample)]
struct Config {
    /// Config.a should be a number
    a: usize,
    /// Config.b should be a string
    b: String,
    /// Optional Config.c is a number
    c: Option<usize>,
    /// Config.d is a list of number
    d: Vec<usize>,
    #[toml_example(default =7)]
    e: usize,
    /// Config.f should be a string
    #[toml_example(default = "seven")]
    f: String,
}
assert_eq!( Config::toml_example(),
r#"# 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
a = 0

# Config.b should be a string
b = ""

# Optional Config.c is a number
# c = 0

# Config.d is a list of number
d = [ 0, ]

e = 7

# Config.f should be a string
f = "seven"

"#);

Also, toml-example will use #[serde(default)], #[serde(default = "default_fn")] for the example value.

With nesting structure, #[toml_example(nesting)] should set on the field as following example.

use std::collections::HashMap;
use toml_example::TomlExample;

/// Service with specific port
#[derive(TomlExample)]
struct Service {
    /// port should be a number
    #[toml_example(default = 80)]
    port: usize,
}
#[derive(TomlExample)]
#[allow(dead_code)]
struct Node {
    /// Services are running in the node
    #[toml_example(default = http, nesting)]
    services: HashMap<String, Service>,
}

assert_eq!(Node::toml_example(),
r#"# Services are running in the node
# Service with specific port
[services.http]
# port should be a number
port = 80

"#);

Flattened items are supported as well.

use toml_example::TomlExample;

#[derive(TomlExample)]
struct ItemWrapper {
    #[toml_example(flatten, nesting)]
    item: Item,
}
#[derive(TomlExample)]
struct Item {
    value: String,
}

assert_eq!(ItemWrapper::toml_example(), Item::toml_example());

Flattening works with maps too!

use serde::Deserialize;
use toml_example::TomlExample;

#[derive(TomlExample, Deserialize)]
struct MainConfig {
    #[serde(flatten)]
    #[toml_example(nesting)]
    nested: HashMap<String, ConfigItem>,
}
#[derive(TomlExample, Deserialize)]
struct ConfigItem {
    #[toml_example(default = false)]
    enabled: bool,
}

let example = MainConfig::toml_example();
assert!(toml::from_str::<MainConfig>(&example).is_ok());
assert_eq!(example, r#"[example]
enabled = false

"#);

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 serde::Serialize;
use toml_example::TomlExample;

#[derive(TomlExample, Serialize)]
#[serde(default)]
struct Config {
    /// Name of the theme to use
    theme: String,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            theme: String::from("Dark"),
        }
    }
}

assert_eq!(Config::toml_example(),
r#"# Name of the theme to use
theme = "Dark"

"#);

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 toml_example::TomlExample;
#[derive(TomlExample)]
struct Config {
    /// Config.a is an optional number
    #[toml_example(require)]
    a: Option<usize>,
    /// Config.b is an optional string
    #[toml_example(require)]
    b: Option<String>,
    #[toml_example(require, default = "third")]
    c: Option<String>,
    #[toml_example(skip)]
    d: usize,
}
assert_eq!(Config::toml_example(),
r#"# Config.a is an optional number
a = 0

# Config.b is an optional string
b = ""

c = "third"

"#)

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 toml_example::TomlExample;
#[derive(TomlExample)]
struct Config {
    /// Config.priority is an enum
    #[toml_example(default, enum)]
    priority: Priority,
}
#[derive(Debug, Default)]
enum Priority {
    #[default]
    Important,
    Trivial,
}
assert_eq!(Config::toml_example(),
r#"# Config.priority is an enum
priority = "Important"

"#)

Rust 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:

#[derive(TomlExample)]
// We have to use double backslash here, else it will fuck up the AST
#[toml_example(doc_skip_prefix = "\\")]
struct Config {
    /// \ This comment is not added to the TOML
    /// This comment is added to the TOML
    a: String,
    #[toml_example(doc_skip_prefix = "dev-doc:")]
    /// This is a TOML comment
    /// dev-doc: We can add additional prefixes.
    b: String,
}
assert_eq!(Config::toml_example(),
r#"# This comment is added to the TOML
a = ""

# This is a TOML comment
b = ""

"#)

You can use #[toml_example(help = "...")] to override the doc string with a custom help text for the TOML example. This works on both struct fields and structs themselves. When help is set, the doc string is ignored and the help text is used as the TOML comment.

use toml_example::TomlExample;
#[derive(TomlExample)]
struct Config {
    /// Config.a should be a number
    #[toml_example(help = "The port number to listen on")]
    a: usize,
}
assert_eq!(Config::toml_example(),
r#"# The port number to listen on
a = 0

"#)

Re-exports§

pub use traits::*;

Modules§

traits