aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs2
-rw-r--r--src/ser.rs231
-rw-r--r--tests/pretty.rs168
3 files changed, 391 insertions, 10 deletions
diff --git a/src/lib.rs b/src/lib.rs
index ee4e961..c4a7e9d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -161,7 +161,7 @@ mod datetime;
pub mod ser;
#[doc(no_inline)]
-pub use ser::{to_string, to_vec, Serializer};
+pub use ser::{to_string, to_string_pretty, to_vec, Serializer};
pub mod de;
#[doc(no_inline)]
pub use de::{from_slice, from_str, Deserializer};
diff --git a/src/ser.rs b/src/ser.rs
index 77db867..863bd75 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -93,6 +93,18 @@ pub fn to_string<T: ?Sized>(value: &T) -> Result<String, Error>
Ok(dst)
}
+/// Serialize the given data structure as a "pretty" String of TOML.
+///
+/// This is identical to `to_string` except the output string has a more
+/// "pretty" output. See `Serializer::pretty` for more details.
+pub fn to_string_pretty<T: ?Sized>(value: &T) -> Result<String, Error>
+ where T: ser::Serialize,
+{
+ let mut dst = String::with_capacity(128);
+ value.serialize(&mut Serializer::pretty(&mut dst))?;
+ Ok(dst)
+}
+
/// Errors that can occur when serializing a type.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Error {
@@ -137,6 +149,31 @@ pub enum Error {
__Nonexhaustive,
}
+#[derive(Debug, Default, Clone)]
+#[doc(hidden)]
+/// Internal place for holding array setings
+struct ArraySettings {
+ indent: usize,
+ trailing_comma: bool,
+}
+
+impl ArraySettings {
+ fn pretty() -> ArraySettings {
+ ArraySettings {
+ indent: 4,
+ trailing_comma: true,
+ }
+ }
+}
+
+#[derive(Debug, Default, Clone)]
+#[doc(hidden)]
+/// Internal struct for holding serialization settings
+struct Settings {
+ array: Option<ArraySettings>,
+ pretty_string: bool,
+}
+
/// Serialization implementation for TOML.
///
/// This structure implements serialization support for TOML to serialize an
@@ -149,6 +186,7 @@ pub enum Error {
pub struct Serializer<'a> {
dst: &'a mut String,
state: State<'a>,
+ settings: Settings,
}
#[derive(Debug, Clone)]
@@ -194,9 +232,130 @@ impl<'a> Serializer<'a> {
Serializer {
dst: dst,
state: State::End,
+ settings: Settings::default(),
+ }
+ }
+
+ /// Instantiate a "pretty" formatter
+ ///
+ /// By default this will use:
+ ///
+ /// - pretty strings: strings with newlines will use the `'''` syntax. See
+ /// `Serializer::pretty_string`
+ /// - pretty arrays: each item in arrays will be on a newline, have an indentation of 4 and
+ /// have a trailing comma. See `Serializer::pretty_array`
+ pub fn pretty(dst: &'a mut String) -> Serializer<'a> {
+ Serializer {
+ dst: dst,
+ state: State::End,
+ settings: Settings {
+ array: Some(ArraySettings::pretty()),
+ pretty_string: true,
+ },
}
}
+ /// Enable or Disable pretty strings
+ ///
+ /// If enabled, strings with one or more newline character will use the `'''` syntax.
+ ///
+ /// # Examples
+ ///
+ /// Instead of:
+ ///
+ /// ```ignore
+ /// single = "no newlines"
+ /// text = "\nfoo\nbar\n"
+ /// ```
+ ///
+ /// You will have:
+ ///
+ /// ```ignore
+ /// single = "no newlines"
+ /// text = '''
+ /// foo
+ /// bar
+ /// '''
+ /// ```
+ pub fn pretty_string(&mut self, value: bool) -> &mut Self {
+ self.settings.pretty_string = value;
+ self
+ }
+
+ /// Enable or Disable pretty arrays
+ ///
+ /// If enabled, arrays will always have each item on their own line.
+ ///
+ /// Some specific features can be controlled via other builder methods:
+ ///
+ /// - `Serializer::pretty_array_indent`: set the indent to a value other
+ /// than 4.
+ /// - `Serializer::pretty_array_trailing_comma`: enable/disable the trailing
+ /// comma on the last item.
+ ///
+ /// # Examples
+ ///
+ /// Instead of:
+ ///
+ /// ```ignore
+ /// array = ["foo", "bar"]
+ /// ```
+ ///
+ /// You will have:
+ ///
+ /// ```ignore
+ /// array = [
+ /// "foo",
+ /// "bar",
+ /// ]
+ /// ```
+ pub fn pretty_array(&mut self, value: bool) -> &mut Self {
+ self.settings.array = if value {
+ Some(ArraySettings::pretty())
+ } else {
+ None
+ };
+ self
+ }
+
+ /// Set the indent for pretty arrays
+ ///
+ /// See `Serializer::pretty_array` for more details.
+ pub fn pretty_array_indent(&mut self, value: usize) -> &mut Self {
+ let use_default = if let &mut Some(ref mut a) = &mut self.settings.array {
+ a.indent = value;
+ false
+ } else {
+ true
+ };
+
+ if use_default {
+ let mut array = ArraySettings::pretty();
+ array.indent = value;
+ self.settings.array = Some(array);
+ }
+ self
+ }
+
+ /// Specify whether to use a trailing comma when serializing pretty arrays
+ ///
+ /// See `Serializer::pretty_array` for more details.
+ pub fn pretty_array_trailing_comma(&mut self, value: bool) -> &mut Self {
+ let use_default = if let &mut Some(ref mut a) = &mut self.settings.array {
+ a.trailing_comma = value;
+ false
+ } else {
+ true
+ };
+
+ if use_default {
+ let mut array = ArraySettings::pretty();
+ array.trailing_comma = value;
+ self.settings.array = Some(array);
+ }
+ self
+ }
+
fn display<T: fmt::Display>(&mut self,
t: T,
type_: &'static str) -> Result<(), Error> {
@@ -241,10 +400,24 @@ impl<'a> Serializer<'a> {
}
fn emit_array(&mut self, first: &Cell<bool>) -> Result<(), Error> {
- if first.get() {
- self.dst.push_str("[");
- } else {
- self.dst.push_str(", ");
+ match self.settings.array {
+ Some(ref a) => {
+ if first.get() {
+ self.dst.push_str("[\n")
+ } else {
+ self.dst.push_str(",\n")
+ }
+ for _ in 0..a.indent {
+ self.dst.push_str(" ");
+ }
+ },
+ None => {
+ if first.get() {
+ self.dst.push_str("[")
+ } else {
+ self.dst.push_str(", ")
+ }
+ },
}
Ok(())
}
@@ -283,15 +456,36 @@ impl<'a> Serializer<'a> {
}
fn emit_str(&mut self, value: &str) -> Result<(), Error> {
- drop(write!(self.dst, "\""));
+ let do_pretty = if self.settings.pretty_string {
+ value.contains('\n')
+ } else {
+ false
+ };
+ if do_pretty {
+ drop(write!(self.dst, "'''\n"));
+ } else {
+ drop(write!(self.dst, "\""));
+ }
for ch in value.chars() {
match ch {
'\u{8}' => drop(write!(self.dst, "\\b")),
'\u{9}' => drop(write!(self.dst, "\\t")),
- '\u{a}' => drop(write!(self.dst, "\\n")),
+ '\u{a}' => {
+ if do_pretty {
+ drop(write!(self.dst, "\n"));
+ } else {
+ drop(write!(self.dst, "\\n"));
+ }
+ },
'\u{c}' => drop(write!(self.dst, "\\f")),
'\u{d}' => drop(write!(self.dst, "\\r")),
- '\u{22}' => drop(write!(self.dst, "\\\"")),
+ '\u{22}' => {
+ if do_pretty {
+ drop(write!(self.dst, "\""))
+ } else {
+ drop(write!(self.dst, "\\\""))
+ }
+ },
'\u{5c}' => drop(write!(self.dst, "\\\\")),
c if c < '\u{1f}' => {
drop(write!(self.dst, "\\u{:04X}", ch as u32))
@@ -299,7 +493,11 @@ impl<'a> Serializer<'a> {
ch => drop(write!(self.dst, "{}", ch)),
}
}
- drop(write!(self.dst, "\""));
+ if do_pretty {
+ drop(write!(self.dst, "'''"));
+ } else {
+ drop(write!(self.dst, "\""));
+ }
Ok(())
}
@@ -591,6 +789,7 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> {
first: &self.first,
type_: &self.type_,
},
+ settings: self.ser.settings.clone(),
})?;
self.first.set(false);
Ok(())
@@ -599,7 +798,19 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> {
fn end(self) -> Result<(), Error> {
match self.type_.get() {
Some("table") => return Ok(()),
- Some(_) => self.ser.dst.push_str("]"),
+ Some(_) => {
+ match self.ser.settings.array {
+ Some(ref a) => {
+ if a.trailing_comma {
+ self.ser.dst.push_str(",");
+ }
+ self.ser.dst.push_str("\n]");
+ },
+ None => {
+ self.ser.dst.push_str("]");
+ }
+ }
+ }
None => {
assert!(self.first.get());
self.ser.emit_key("array")?;
@@ -650,6 +861,7 @@ impl<'a, 'b> ser::SerializeMap for SerializeTable<'a, 'b> {
first: first,
table_emitted: table_emitted,
},
+ settings: ser.settings.clone(),
});
match res {
Ok(()) => first.set(false),
@@ -705,6 +917,7 @@ impl<'a, 'b> ser::SerializeStruct for SerializeTable<'a, 'b> {
first: first,
table_emitted: table_emitted,
},
+ settings: ser.settings.clone(),
});
match res {
Ok(()) => first.set(false),
diff --git a/tests/pretty.rs b/tests/pretty.rs
new file mode 100644
index 0000000..ae9a839
--- /dev/null
+++ b/tests/pretty.rs
@@ -0,0 +1,168 @@
+extern crate toml;
+extern crate serde;
+
+use serde::ser::Serialize;
+
+const NO_PRETTY: &'static str = "\
+[example]
+array = [\"item 1\", \"item 2\"]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+
+#[test]
+fn no_pretty() {
+ let toml = NO_PRETTY;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ value.serialize(&mut toml::Serializer::new(&mut result)).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+#[test]
+fn disable_pretty() {
+ let toml = NO_PRETTY;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_string(false);
+ serializer.pretty_array(false);
+ value.serialize(&mut serializer).unwrap();
+ }
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_STD: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\",
+]
+empty = []
+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::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ value.serialize(&mut toml::Serializer::pretty(&mut result)).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+
+const PRETTY_INDENT_2: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\",
+]
+empty = []
+oneline = \"this has no newlines.\"
+text = '''
+this is the first line
+this is the second line
+'''
+";
+
+#[test]
+fn pretty_indent_2() {
+ let toml = PRETTY_INDENT_2;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_array_indent(2);
+ value.serialize(&mut serializer).unwrap();
+ }
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_INDENT_2_OTHER: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\",
+]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+
+
+#[test]
+/// Test pretty indent when gotten the other way
+fn pretty_indent_2_other() {
+ let toml = PRETTY_INDENT_2_OTHER;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::new(&mut result);
+ serializer.pretty_array_indent(2);
+ value.serialize(&mut serializer).unwrap();
+ }
+ assert_eq!(toml, &result);
+}
+
+
+const PRETTY_ARRAY_NO_COMMA: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\"
+]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+#[test]
+/// Test pretty indent when gotten the other way
+fn pretty_indent_array_no_comma() {
+ let toml = PRETTY_ARRAY_NO_COMMA;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::new(&mut result);
+ serializer.pretty_array_trailing_comma(false);
+ value.serialize(&mut serializer).unwrap();
+ }
+ assert_eq!(toml, &result);
+}
+
+
+const PRETTY_NO_STRING: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\",
+]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+#[test]
+/// Test pretty indent when gotten the other way
+fn pretty_no_string() {
+ let toml = PRETTY_NO_STRING;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_string(false);
+ value.serialize(&mut serializer).unwrap();
+ }
+ assert_eq!(toml, &result);
+}