Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ members = [
"lib",
"derive",
]
resolver = "2"

[workspace.package]
version = "0.15.0"
edition = "2021"
authors = ["Antonio Yang <yanganto@gmail.com>"]
authors = ["Antonio Yang <yanganto@gmail.com>", "faervan <faervan@proton.me>"]
license = "MIT"
description = "A lib help generate toml example"
repository = "https://github.com/yanganto/toml-example"
Expand Down
89 changes: 67 additions & 22 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,39 @@ impl ParsedField {
}

fn label(&self) -> String {
let label = match self.nesting_format {
Some(NestingFormat::Section(NestingType::Dict)) => {
if self.flatten {
self.default_key()
} else {
format!("{}.{}", self.name, self.default_key())
}
}
Some(NestingFormat::Prefix) => String::new(),
_ => {
if self.flatten {
String::new()
} else {
self.name.to_string()
}
}
};
if label.is_empty() {
String::from("label")
} else {
format!(
"
if label.is_empty() {{
\"{label}\".to_string()
}} else {{
label.to_string() + \".\" + \"{label}\"
}}
"
)
}
}

fn label_format(&self) -> (&str, &str) {
match self.nesting_format {
Some(NestingFormat::Section(NestingType::Vec)) => {
if self.flatten {
Expand All @@ -78,22 +111,15 @@ impl ParsedField {
)
)
}
self.prefix() + &format!("[[{}]]", self.name)
}
Some(NestingFormat::Section(NestingType::Dict)) => {
self.prefix()
+ &if self.flatten {
format!("[{}]", self.default_key())
} else {
format!("[{}.{}]", self.name, self.default_key())
}
("[[", "]]")
}
Some(NestingFormat::Prefix) => "".to_string(),
Some(NestingFormat::Section(NestingType::Dict)) => ("[", "]"),
Some(NestingFormat::Prefix) => ("", ""),
_ => {
if self.flatten {
self.prefix()
("", "")
} else {
self.prefix() + &format!("[{}]", self.name)
("[", "]")
}
}
}
Expand Down Expand Up @@ -453,10 +479,16 @@ impl Intermediate {
Ok(quote! {
impl toml_example::TomlExample for #struct_name {
fn toml_example() -> String {
#struct_name::toml_example_with_prefix("", "")
#struct_name::toml_example_with_prefix("", ("", ""), "")
}
fn toml_example_with_prefix(label: &str, prefix: &str) -> String {
#struct_doc.to_string() + label + &#field_example_stream
fn toml_example_with_prefix(label: &str, label_format: (&str, &str), prefix: &str)
-> String {
let wrapped_label = if label_format.0.is_empty() {
String::new()
} else {
label_format.0.to_string() + label + label_format.1
};
#struct_doc.to_string() + &wrapped_label + &#field_example_stream
}
}
})
Expand Down Expand Up @@ -485,27 +517,40 @@ impl Intermediate {
let (example, nesting_section_newline) =
if field.nesting_format == Some(NestingFormat::Prefix) {
(&mut field_example, "")
} else {
} else if field.flatten {
(
&mut nesting_field_example,
if field.flatten
&& field.nesting_format
== Some(NestingFormat::Section(NestingType::None))
&mut field_example,
if field.nesting_format
== Some(NestingFormat::Section(NestingType::None))
{
""
} else {
"\n"
},
)
} else {
(&mut nesting_field_example, "\n")
};

field.push_doc_to_string(example);
if let Some(ref field_type) = field.ty {
example.push_str("\"##.to_string()");
let (before, after) = field.label_format();
let label_format = format!(
"(\"{}{before}\", \"{after}{nesting_section_newline}\")",
if field.optional && field.nesting_format != Some(NestingFormat::Prefix)
{
"# "
} else {
""
}
);
example.push_str(&format!(
" + &{field_type}::toml_example_with_prefix(\"{}{}\", \"{}\")",
" + &{field_type}::toml_example_with_prefix(\
&{}, {}, \"{}\"\
)",
field.label(),
nesting_section_newline,
label_format,
field.prefix()
));
example.push_str(" + &r##\"");
Expand Down
109 changes: 109 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,91 @@ port = 80
assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
}

#[test]
fn recursive_nesting() {
#[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
struct Outer {
#[toml_example(nesting)]
_middle: Middle,
}
#[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
struct Middle {
#[toml_example(nesting)]
_inner: Inner,
}
#[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
struct Inner {
_value: usize,
}
let example = Outer::toml_example();
assert_eq!(toml::from_str::<Outer>(&example).unwrap(), Outer::default());
assert_eq!(
example,
r#"[_middle]
[_middle._inner]
_value = 0

"#
);
}

#[test]
fn recursive_nesting_and_flatten() {
#[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
struct Outer {
#[toml_example(nesting)]
middle: Middle,
#[toml_example(default = false)]
/// Some toggle
flag: bool,
}
#[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
struct Middle {
#[serde(flatten)]
#[toml_example(nesting)]
/// Values of [Inner] are flattened into [Middle]
inner: Inner,
}
#[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
struct Inner {
#[toml_example(nesting)]
/// [Extra] is flattened into [Middle]
extra: Extra,
/// `value` is defined below `extra`, but shown above
value: usize,
}
#[derive(TomlExample, Debug, Deserialize, PartialEq)]
#[toml_example(default)]
struct Extra {
name: String,
}
impl Default for Extra {
fn default() -> Self {
Self {
name: String::from("ferris"),
}
}
}
let example = Outer::toml_example();
assert_eq!(toml::from_str::<Outer>(&example).unwrap(), Outer::default());
assert_eq!(
example,
r#"# Some toggle
flag = false

[middle]
# Values of [Inner] are flattened into [Middle]
# `value` is defined below `extra`, but shown above
value = 0

# [Extra] is flattened into [Middle]
[middle.extra]
name = "ferris"

"#
);
}

#[test]
fn require() {
#[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
Expand Down Expand Up @@ -1039,6 +1124,30 @@ ab3 = "B"
assert_eq!(ItemWrapper::toml_example(), Item::toml_example());
}

#[test]
fn flatten_order() {
#[derive(TomlExample)]
struct Outer {
#[toml_example(nesting)]
_nested: Item,
#[toml_example(flatten, nesting)]
_flattened: Item,
}
#[derive(TomlExample)]
struct Item {
_value: String,
}
assert_eq!(
Outer::toml_example(),
r#"_value = ""

[_nested]
_value = ""

"#
);
}

#[test]
fn multi_attr_escaping() {
#[derive(TomlExample, Deserialize, PartialEq)]
Expand Down
2 changes: 1 addition & 1 deletion lib/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::Path;
pub trait TomlExample {
/// structure to toml example
fn toml_example() -> String;
fn toml_example_with_prefix(label: &str, prefix: &str) -> String;
fn toml_example_with_prefix(label: &str, label_format: (&str, &str), prefix: &str) -> String;
fn to_toml_example<P: AsRef<Path>>(file_name: P) -> std::io::Result<()> {
let mut file = File::create(file_name)?;
file.write_all(Self::toml_example().as_bytes())?;
Expand Down