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;
|
||||
#[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};
|
||||
|
|
231
src/ser.rs
231
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),
|
||||
|
|
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