Merge pull request #197 from vitiral/pretty
close #119: allow "pretty" configuration
This commit is contained in:
commit
05a8c73439
|
@ -161,7 +161,7 @@ mod datetime;
|
||||||
|
|
||||||
pub mod ser;
|
pub mod ser;
|
||||||
#[doc(no_inline)]
|
#[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;
|
pub mod de;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use de::{from_slice, from_str, Deserializer};
|
pub use de::{from_slice, from_str, Deserializer};
|
||||||
|
|
223
src/ser.rs
223
src/ser.rs
|
@ -93,6 +93,18 @@ pub fn to_string<T: ?Sized>(value: &T) -> Result<String, Error>
|
||||||
Ok(dst)
|
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.
|
/// Errors that can occur when serializing a type.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -137,6 +149,31 @@ pub enum Error {
|
||||||
__Nonexhaustive,
|
__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.
|
/// Serialization implementation for TOML.
|
||||||
///
|
///
|
||||||
/// This structure implements serialization support for TOML to serialize an
|
/// This structure implements serialization support for TOML to serialize an
|
||||||
|
@ -149,6 +186,7 @@ pub enum Error {
|
||||||
pub struct Serializer<'a> {
|
pub struct Serializer<'a> {
|
||||||
dst: &'a mut String,
|
dst: &'a mut String,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
|
settings: Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -194,9 +232,130 @@ impl<'a> Serializer<'a> {
|
||||||
Serializer {
|
Serializer {
|
||||||
dst: dst,
|
dst: dst,
|
||||||
state: State::End,
|
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,
|
fn display<T: fmt::Display>(&mut self,
|
||||||
t: T,
|
t: T,
|
||||||
type_: &'static str) -> Result<(), Error> {
|
type_: &'static str) -> Result<(), Error> {
|
||||||
|
@ -241,10 +400,24 @@ impl<'a> Serializer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_array(&mut self, first: &Cell<bool>) -> Result<(), Error> {
|
fn emit_array(&mut self, first: &Cell<bool>) -> Result<(), Error> {
|
||||||
|
match self.settings.array {
|
||||||
|
Some(ref a) => {
|
||||||
if first.get() {
|
if first.get() {
|
||||||
self.dst.push_str("[");
|
self.dst.push_str("[\n")
|
||||||
} else {
|
} else {
|
||||||
self.dst.push_str(", ");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -283,15 +456,36 @@ impl<'a> Serializer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_str(&mut self, value: &str) -> Result<(), Error> {
|
fn emit_str(&mut self, value: &str) -> Result<(), Error> {
|
||||||
|
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, "\""));
|
drop(write!(self.dst, "\""));
|
||||||
|
}
|
||||||
for ch in value.chars() {
|
for ch in value.chars() {
|
||||||
match ch {
|
match ch {
|
||||||
'\u{8}' => drop(write!(self.dst, "\\b")),
|
'\u{8}' => drop(write!(self.dst, "\\b")),
|
||||||
'\u{9}' => drop(write!(self.dst, "\\t")),
|
'\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{c}' => drop(write!(self.dst, "\\f")),
|
||||||
'\u{d}' => drop(write!(self.dst, "\\r")),
|
'\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, "\\\\")),
|
'\u{5c}' => drop(write!(self.dst, "\\\\")),
|
||||||
c if c < '\u{1f}' => {
|
c if c < '\u{1f}' => {
|
||||||
drop(write!(self.dst, "\\u{:04X}", ch as u32))
|
drop(write!(self.dst, "\\u{:04X}", ch as u32))
|
||||||
|
@ -299,7 +493,11 @@ impl<'a> Serializer<'a> {
|
||||||
ch => drop(write!(self.dst, "{}", ch)),
|
ch => drop(write!(self.dst, "{}", ch)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if do_pretty {
|
||||||
|
drop(write!(self.dst, "'''"));
|
||||||
|
} else {
|
||||||
drop(write!(self.dst, "\""));
|
drop(write!(self.dst, "\""));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,6 +789,7 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> {
|
||||||
first: &self.first,
|
first: &self.first,
|
||||||
type_: &self.type_,
|
type_: &self.type_,
|
||||||
},
|
},
|
||||||
|
settings: self.ser.settings.clone(),
|
||||||
})?;
|
})?;
|
||||||
self.first.set(false);
|
self.first.set(false);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -599,7 +798,19 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> {
|
||||||
fn end(self) -> Result<(), Error> {
|
fn end(self) -> Result<(), Error> {
|
||||||
match self.type_.get() {
|
match self.type_.get() {
|
||||||
Some("table") => return Ok(()),
|
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 => {
|
None => {
|
||||||
assert!(self.first.get());
|
assert!(self.first.get());
|
||||||
self.ser.emit_key("array")?;
|
self.ser.emit_key("array")?;
|
||||||
|
@ -650,6 +861,7 @@ impl<'a, 'b> ser::SerializeMap for SerializeTable<'a, 'b> {
|
||||||
first: first,
|
first: first,
|
||||||
table_emitted: table_emitted,
|
table_emitted: table_emitted,
|
||||||
},
|
},
|
||||||
|
settings: ser.settings.clone(),
|
||||||
});
|
});
|
||||||
match res {
|
match res {
|
||||||
Ok(()) => first.set(false),
|
Ok(()) => first.set(false),
|
||||||
|
@ -705,6 +917,7 @@ impl<'a, 'b> ser::SerializeStruct for SerializeTable<'a, 'b> {
|
||||||
first: first,
|
first: first,
|
||||||
table_emitted: table_emitted,
|
table_emitted: table_emitted,
|
||||||
},
|
},
|
||||||
|
settings: ser.settings.clone(),
|
||||||
});
|
});
|
||||||
match res {
|
match res {
|
||||||
Ok(()) => first.set(false),
|
Ok(()) => first.set(false),
|
||||||
|
|
168
tests/pretty.rs
Normal file
168
tests/pretty.rs
Normal file
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in a new issue