#[doc(hidden)]
pub use toml_example_derive::TomlExample;
pub mod traits;
pub use traits::*;
#[cfg(test)]
mod tests {
use crate as toml_example;
use serde_derive::Deserialize;
use std::collections::HashMap;
use toml_example::TomlExample;
#[test]
fn basic() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
a: usize,
b: String,
}
assert_eq!(
Config::toml_example(),
r#"# Config.a should be a number
a = 0
# Config.b should be a string
b = ""
"#
);
assert_eq!(
toml::from_str::<Config>(&Config::toml_example()).unwrap(),
Config::default()
);
let mut tmp_file = std::env::temp_dir();
tmp_file.push("config.toml");
Config::to_toml_example(&tmp_file.as_path().to_str().unwrap()).unwrap();
assert_eq!(
std::fs::read_to_string(tmp_file).unwrap(),
r#"# Config.a should be a number
a = 0
# Config.b should be a string
b = ""
"#
);
}
#[test]
fn option() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
a: Option<usize>,
b: Option<String>,
}
assert_eq!(
Config::toml_example(),
r#"# Config.a is an optional number
# a = 0
# Config.b is an optional string
# b = ""
"#
);
assert_eq!(
toml::from_str::<Config>(&Config::toml_example()).unwrap(),
Config::default()
)
}
#[test]
fn vec() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
a: Vec<usize>,
b: Vec<String>,
c: Vec<Option<usize>>,
d: Option<Vec<usize>>,
}
assert_eq!(
Config::toml_example(),
r#"# Config.a is a list of number
a = [ 0, ]
# Config.b is a list of string
b = [ "", ]
# Config.c
c = [ 0, ]
# Config.d
# d = [ 0, ]
"#
);
assert!(toml::from_str::<Config>(&Config::toml_example()).is_ok())
}
#[test]
fn struct_doc() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
a: usize,
}
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
# the number should be greater or equal zero
a = 0
"#
);
assert_eq!(
toml::from_str::<Config>(&Config::toml_example()).unwrap(),
Config::default()
)
}
#[test]
fn serde_default() {
fn default_a() -> usize {
7
}
fn default_b() -> String {
"default".into()
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
#[serde(default = "default_a")]
a: usize,
#[serde(default = "default_b")]
b: String,
#[serde(default)]
c: usize,
#[serde(default)]
d: String,
#[serde(default)]
e: Option<usize>,
}
assert_eq!(
Config::toml_example(),
r#"# Config.a should be a number
a = 7
# Config.b should be a string
b = "default"
# Config.c should be a number
c = 0
# Config.d should be a string
d = ""
# e = 0
"#
);
}
#[test]
fn toml_example_default() {
fn default_str() -> String {
"seven".into()
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
#[toml_example(default = 7)]
a: usize,
#[toml_example(default = "default")]
#[serde(default = "default_str")]
b: String,
#[serde(default = "default_str")]
#[toml_example(default = "default")]
c: String,
#[toml_example(default = [ "default", ])]
e: Vec<String>,
#[toml_example(
default = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
)]
f: String,
#[toml_example(default = [ "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
"second",
"third",
])]
g: Vec<String>,
#[toml_example(default = "#FAFAFA")]
color: String,
}
assert_eq!(
Config::toml_example(),
r##"# Config.a should be a number
a = 7
# Config.b should be a string
b = "seven"
c = "default"
e = ["default",]
f = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
g = ["super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
"second", "third",]
# Config.color should be a hex color code
color = "#FAFAFA"
"##
);
}
#[test]
fn no_nesting() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Inner {
a: usize,
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Outer {
inner: Inner,
}
assert_eq!(
Outer::toml_example(),
r#"# Outer.inner is a complex struct
inner = ""
"#
);
}
#[test]
fn nesting() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Inner {
a: usize,
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Outer {
#[toml_example(nesting)]
inner: Inner,
}
assert_eq!(
Outer::toml_example(),
r#"# Outer.inner is a complex struct
# Inner is a config live in Outer
[inner]
# Inner.a should be a number
a = 0
"#
);
assert_eq!(
toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
Outer::default()
);
}
#[test]
fn nesting_by_section() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Inner {
a: usize,
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Outer {
#[toml_example(nesting = section)]
inner: Inner,
}
assert_eq!(
Outer::toml_example(),
r#"# Outer.inner is a complex struct
# Inner is a config live in Outer
[inner]
# Inner.a should be a number
a = 0
"#
);
assert_eq!(
toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
Outer::default()
);
}
#[test]
fn nesting_by_prefix() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Inner {
a: usize,
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Outer {
#[toml_example(nesting = prefix)]
inner: Inner,
}
assert_eq!(
Outer::toml_example(),
r#"# Outer.inner is a complex struct
# Inner is a config live in Outer
# Inner.a should be a number
inner.a = 0
"#
);
assert_eq!(
toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
Outer::default()
);
}
#[test]
fn nesting_vector() {
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Service {
port: usize,
}
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Node {
#[toml_example(nesting)]
services: Vec<Service>,
}
assert_eq!(
Node::toml_example(),
r#"# Services are running in the node
# Service with specific port
[[services]]
# port should be a number
port = 0
"#
);
assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
}
#[test]
fn nesting_hashmap() {
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Service {
port: usize,
}
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Node {
#[toml_example(nesting)]
services: HashMap<String, Service>,
}
assert_eq!(
Node::toml_example(),
r#"# Services are running in the node
# Service with specific port
[services.example]
# port should be a number
port = 0
"#
);
assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
}
#[test]
fn optional_nesting() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Inner {
a: usize,
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Outer {
#[toml_example(nesting)]
inner: Option<Inner>,
}
assert_eq!(
Outer::toml_example(),
r#"# Outer.inner is a complex struct
# Inner is a config live in Outer
# [inner]
# Inner.a should be a number
# a = 0
"#
);
assert_eq!(
toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
Outer::default()
);
}
#[test]
fn optional_nesting_by_section() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Inner {
a: usize,
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Outer {
#[toml_example(nesting = section)]
inner: Option<Inner>,
}
assert_eq!(
Outer::toml_example(),
r#"# Outer.inner is a complex struct
# Inner is a config live in Outer
# [inner]
# Inner.a should be a number
# a = 0
"#
);
assert_eq!(
toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
Outer::default()
);
}
#[test]
fn optional_nesting_by_prefix() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Inner {
a: usize,
}
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Outer {
#[toml_example(nesting = prefix)]
inner: Option<Inner>,
}
assert_eq!(
Outer::toml_example(),
r#"# Outer.inner is a complex struct
# Inner is a config live in Outer
# Inner.a should be a number
# inner.a = 0
"#
);
assert_eq!(
toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
Outer::default()
);
}
#[test]
fn optional_nesting_vector() {
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Service {
port: usize,
}
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Node {
#[toml_example(nesting)]
services: Option<Vec<Service>>,
}
assert_eq!(
Node::toml_example(),
r#"# Services are running in the node
# Service with specific port
# [[services]]
# port should be a number
# port = 0
"#
);
assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
}
#[test]
fn optional_nesting_hashmap() {
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Service {
port: usize,
}
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Node {
#[toml_example(nesting)]
services: Option<HashMap<String, Service>>,
}
assert_eq!(
Node::toml_example(),
r#"# Services are running in the node
# Service with specific port
# [services.example]
# port should be a number
# port = 0
"#
);
assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
}
#[test]
fn nesting_hashmap_with_default_name() {
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Service {
#[toml_example(default = 80)]
port: usize,
}
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Node {
#[toml_example(nesting)]
#[toml_example(default = http)]
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
"#
);
assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
}
#[test]
fn nesting_hashmap_with_dash_name() {
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Service {
#[toml_example(default = 80)]
port: usize,
}
#[derive(TomlExample, Deserialize)]
#[allow(dead_code)]
struct Node {
#[toml_example(nesting)]
#[toml_example(default = http.01)]
services: HashMap<String, Service>,
}
assert_eq!(
Node::toml_example(),
r#"# Services are running in the node
# Service with specific port
[services.http-01]
# port should be a number
port = 80
"#
);
assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
}
#[test]
fn require() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
#[toml_example(require)]
a: Option<usize>,
#[toml_example(require)]
b: Option<String>,
#[toml_example(require)]
#[toml_example(default = "third")]
c: Option<String>,
}
assert_eq!(
Config::toml_example(),
r#"# Config.a is an optional number
a = 0
# Config.b is an optional string
b = ""
c = "third"
"#
);
}
#[test]
fn skip() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
#[allow(dead_code)]
struct Config {
a: usize,
#[toml_example(skip)]
b: usize,
#[serde(skip)]
c: usize,
#[serde(skip_deserializing)]
d: usize,
}
assert_eq!(
Config::toml_example(),
r#"# Config.a is a number
a = 0
"#
);
}
#[test]
fn r_sharp_field() {
#[derive(TomlExample)]
#[allow(dead_code)]
struct Config {
r#type: usize,
}
assert_eq!(
Config::toml_example(),
r#"# Config.type is a number
type = 0
"#
);
}
#[test]
fn non_nesting_field_should_be_first() {
#[derive(TomlExample)]
#[allow(dead_code)]
struct Foo {
a: String,
}
#[derive(TomlExample)]
#[allow(dead_code)]
struct Bar {
#[toml_example(nesting)]
foo: Foo,
b: String,
}
assert_eq!(
Bar::toml_example(),
r#"b = ""
[foo]
a = ""
"#
);
}
#[test]
fn rename() {
use serde::Serialize;
#[derive(Deserialize, Serialize, TomlExample)]
struct Config {
#[serde(rename = "bb")]
b: usize,
}
assert_eq!(
Config::toml_example(),
r#"bb = 0
"#
);
}
#[test]
fn rename_all() {
use serde::Serialize;
#[derive(Deserialize, Serialize, TomlExample)]
#[serde(rename_all = "kebab-case")]
struct Config {
a_a: usize,
}
assert_eq!(
Config::toml_example(),
r#"a-a = 0
"#
);
}
#[test]
fn hashset_and_struct() {
use std::collections::HashMap;
#[derive(TomlExample)]
#[allow(dead_code)]
struct Foo {
a: String,
}
#[derive(TomlExample)]
#[allow(dead_code)]
struct Bar {
#[toml_example(nesting)]
default: Foo,
#[toml_example(nesting)]
instance: HashMap<String, Foo>,
}
assert_eq!(
Bar::toml_example(),
r#"# Default instances doc
[default]
a = ""
# Instances doc
[instance.example]
a = ""
"#
);
}
}