diff --git a/src/item.rs b/src/item.rs
index e3017aa5..ac0601f1 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -28,6 +28,7 @@ impl Item {
self
}
}
+
// TODO: This should be generated by macro or derive
/// Downcasting
impl Item {
@@ -128,19 +129,15 @@ impl Item {
}
/// In-place convert to a value
pub fn make_value(&mut self) {
- let mut other = Item::None;
- std::mem::swap(self, &mut other);
- let mut other = other.into_value().map(Item::Value).unwrap_or(Item::None);
- std::mem::swap(self, &mut other);
+ let other = std::mem::take(self);
+ let other = other.into_value().map(Item::Value).unwrap_or(Item::None);
+ *self = other;
}
/// Casts `self` to table.
pub fn into_table(self) -> Result
{
match self {
Item::Table(t) => Ok(t),
- Item::Value(v) => match v {
- Value::InlineTable(t) => Ok(t.into_table()),
- _ => Err(Item::Value(v)),
- },
+ Item::Value(Value::InlineTable(t)) => Ok(t.into_table()),
_ => Err(self),
}
}
@@ -148,9 +145,36 @@ impl Item {
pub fn into_array_of_tables(self) -> Result {
match self {
Item::ArrayOfTables(a) => Ok(a),
+ Item::Value(Value::Array(a)) => {
+ if a.is_empty() {
+ Err(Item::Value(Value::Array(a)))
+ } else if a.iter().all(|v| v.is_inline_table()) {
+ let mut aot = ArrayOfTables::new();
+ aot.values = a.values;
+ for value in aot.values.iter_mut() {
+ value.make_item();
+ }
+ Ok(aot)
+ } else {
+ Err(Item::Value(Value::Array(a)))
+ }
+ }
_ => Err(self),
}
}
+ // Starting private because the name is unclear
+ pub(crate) fn make_item(&mut self) {
+ let other = std::mem::take(self);
+ let other = match other.into_table().map(crate::Item::Table) {
+ Ok(i) => i,
+ Err(i) => i,
+ };
+ let other = match other.into_array_of_tables().map(crate::Item::ArrayOfTables) {
+ Ok(i) => i,
+ Err(i) => i,
+ };
+ *self = other;
+ }
/// Returns true iff `self` is a value.
pub fn is_value(&self) -> bool {
self.as_value().is_some()
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
index 4f266541..86ea5625 100644
--- a/src/ser/mod.rs
+++ b/src/ser/mod.rs
@@ -5,6 +5,7 @@
mod array;
mod item;
mod key;
+mod pretty;
mod table;
pub(crate) use array::*;
@@ -12,6 +13,8 @@ pub(crate) use item::*;
pub(crate) use key::*;
pub(crate) use table::*;
+use crate::visit_mut::VisitMut;
+
/// Errors that can occur when deserializing a type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Error {
@@ -146,7 +149,9 @@ pub fn to_string_pretty(value: &T) -> Result
where
T: serde::ser::Serialize,
{
- to_document(value).map(|e| e.to_string())
+ let mut document = to_document(value)?;
+ pretty::Pretty.visit_document_mut(&mut document);
+ Ok(document.to_string())
}
/// Serialize the given data structure into a TOML document.
diff --git a/src/ser/pretty.rs b/src/ser/pretty.rs
new file mode 100644
index 00000000..fa4da4e3
--- /dev/null
+++ b/src/ser/pretty.rs
@@ -0,0 +1,56 @@
+pub(crate) struct Pretty;
+
+impl crate::visit_mut::VisitMut for Pretty {
+ fn visit_document_mut(&mut self, node: &mut crate::Document) {
+ crate::visit_mut::visit_document_mut(self, node);
+ if let Some((_, first)) = node.iter_mut().next() {
+ remove_table_prefix(first);
+ }
+ }
+
+ fn visit_item_mut(&mut self, node: &mut crate::Item) {
+ node.make_item();
+
+ crate::visit_mut::visit_item_mut(self, node);
+ }
+
+ fn visit_table_mut(&mut self, node: &mut crate::Table) {
+ node.decor_mut().clear();
+
+ crate::visit_mut::visit_table_mut(self, node);
+ }
+
+ fn visit_value_mut(&mut self, node: &mut crate::Value) {
+ node.decor_mut().clear();
+
+ crate::visit_mut::visit_value_mut(self, node);
+ }
+
+ fn visit_array_mut(&mut self, node: &mut crate::Array) {
+ crate::visit_mut::visit_array_mut(self, node);
+
+ if (0..=1).contains(&node.len()) {
+ node.set_trailing("");
+ node.set_trailing_comma(false);
+ } else {
+ for item in node.iter_mut() {
+ item.decor_mut().set_prefix("\n ");
+ }
+ node.set_trailing("\n");
+ node.set_trailing_comma(true);
+ }
+ }
+}
+
+fn remove_table_prefix(node: &mut crate::Item) {
+ match node {
+ crate::Item::None => {}
+ crate::Item::Value(_) => {}
+ crate::Item::Table(t) => t.decor_mut().set_prefix(""),
+ crate::Item::ArrayOfTables(a) => {
+ if let Some(first) = a.values.iter_mut().next() {
+ remove_table_prefix(first);
+ }
+ }
+ }
+}
diff --git a/tests/pretty.rs b/tests/pretty.rs
new file mode 100644
index 00000000..40497c25
--- /dev/null
+++ b/tests/pretty.rs
@@ -0,0 +1,110 @@
+#![cfg(feature = "easy")]
+
+use pretty_assertions::assert_eq;
+
+const PRETTY_STD: &'static str = r#"[example]
+array = [
+ "item 1",
+ "item 2",
+]
+empty = []
+one = ["one"]
+oneline = "this has no newlines."
+text = """
+this is the first line
+this is the second line
+"""
+"#;
+
+#[test]
+fn pretty_std() {
+ let toml = PRETTY_STD;
+ let value: toml_edit::easy::Value = toml_edit::easy::from_str(toml).unwrap();
+ let result = toml_edit::easy::to_string_pretty(&value).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_TRICKY: &'static str = r##"[example]
+f = "\f"
+glass = """
+Nothing too unusual, except that I can eat glass in:
+- Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.
+- Polish: Mogę jeść szkło, i mi nie szkodzi.
+- Hindi: मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती.
+- Japanese: 私はガラスを食べられます。それは私を傷つけません。
+"""
+r = "\r"
+r_newline = """
+\r
+"""
+single = "this is a single line but has \"\" cuz it\"s tricky"
+single_tricky = "single line with ''' in it"
+tabs = """
+this is pretty standard
+\texcept for some \ttabs right here
+"""
+text = """
+this is the first line.
+This has a ''' in it and \"\"\" cuz it's tricky yo
+Also ' and \" because why not
+this is the fourth line
+"""
+"##;
+
+#[test]
+fn pretty_tricky() {
+ let toml = PRETTY_TRICKY;
+ let value: toml_edit::easy::Value = toml_edit::easy::from_str(toml).unwrap();
+ let result = toml_edit::easy::to_string_pretty(&value).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_TABLE_ARRAY: &'static str = r##"[[array]]
+key = "foo"
+
+[[array]]
+key = "bar"
+
+[abc]
+doc = "this is a table"
+
+[example]
+single = "this is a single line string"
+"##;
+
+#[test]
+fn pretty_table_array() {
+ let toml = PRETTY_TABLE_ARRAY;
+ let value: toml_edit::easy::Value = toml_edit::easy::from_str(toml).unwrap();
+ let result = toml_edit::easy::to_string_pretty(&value).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const TABLE_ARRAY: &'static str = r##"[[array]]
+key = "foo"
+
+[[array]]
+key = "bar"
+
+[abc]
+doc = "this is a table"
+
+[example]
+single = "this is a single line string"
+"##;
+
+#[test]
+fn table_array() {
+ let toml = TABLE_ARRAY;
+ let value: toml_edit::easy::Value = toml_edit::easy::from_str(toml).unwrap();
+ let result = toml_edit::easy::to_string_pretty(&value).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
diff --git a/tests/serde.rs b/tests/serde.rs
index d9557792..4d1d0d4b 100644
--- a/tests/serde.rs
+++ b/tests/serde.rs
@@ -29,9 +29,15 @@ macro_rules! equivalent {
// Through a string equivalent
println!("to_string(literal)");
- assert_eq!(t!(toml_edit::easy::to_string(&literal)), toml.to_string());
+ assert_eq!(
+ t!(toml_edit::easy::to_string_pretty(&literal)),
+ toml.to_string()
+ );
println!("to_string(toml)");
- assert_eq!(t!(toml_edit::easy::to_string(&toml)), toml.to_string());
+ assert_eq!(
+ t!(toml_edit::easy::to_string_pretty(&toml)),
+ toml.to_string()
+ );
println!("literal, from_str(toml)");
assert_eq!(literal, t!(toml_edit::easy::from_str(&toml.to_string())));
println!("toml, from_str(toml)");