#![allow(dead_code)]
use std::fmt::Write;
use std::fmt;
use toml::{Value, Table};
pub fn pretty_toml(tbl: Table) -> Result<String, fmt::Error> {
let mut out = String::new();
{
let mut pp = PrettyPrinter { output: &mut out, stack: Vec::new() };
try!(pp.print(&tbl));
}
Ok(out)
}
fn write_pretty_str(f: &mut String, s: &str) -> fmt::Result {
try!(write!(f, "'''\n"));
for ch in s.chars() {
match ch {
'\u{8}' => try!(write!(f, "\\b")),
'\u{9}' => try!(write!(f, "\\t")),
'\u{c}' => try!(write!(f, "\\f")),
'\u{d}' => try!(write!(f, "\\r")),
'\u{22}' => try!(write!(f, "\\\"")),
'\u{5c}' => try!(write!(f, "\\\\")),
ch => try!(write!(f, "{}", ch)),
}
}
write!(f, "'''")
}
impl<'a, 'b> PrettyPrinter<'a, 'b> {
fn print(&mut self, table: &'a Table) -> fmt::Result {
let mut space_out_first = false;
for (k, v) in table.iter() {
match *v {
Value::Table(..) => continue,
Value::Array(ref a) => {
if let Some(&Value::Table(..)) = a.first() {
panic!("attempting to serialize an array of tables!")
}
}
Value::String(ref s) => {
if s.contains('\n') {
try!(write!(self.output, "{} = ", Key(&[k])));
try!(write_pretty_str(self.output, s));
try!(write!(self.output, "\n"));
space_out_first = true;
continue;
}
}
_ => {}
}
space_out_first = true;
try!(writeln!(self.output, "{} = {}", Key(&[k]), v));
}
for (i, (k, v)) in table.iter().enumerate() {
if let Value::Table(ref inner) = *v {
self.stack.push(k);
if space_out_first || i != 0 {
try!(write!(self.output, "\n"));
}
try!(writeln!(self.output, "[{}]", Key(&self.stack)));
try!(self.print(inner));
self.stack.pop();
}
}
Ok(())
}
}
struct PrettyPrinter<'a, 'b:'a> {
output: &'b mut String,
stack: Vec<&'a str>,
}
struct Key<'a>(&'a [&'a str]);
fn write_str(f: &mut fmt::Formatter, s: &str) -> fmt::Result {
try!(write!(f, "\""));
for ch in s.chars() {
match ch {
'\u{8}' => try!(write!(f, "\\b")),
'\u{9}' => try!(write!(f, "\\t")),
'\u{a}' => try!(write!(f, "\\n")),
'\u{c}' => try!(write!(f, "\\f")),
'\u{d}' => try!(write!(f, "\\r")),
'\u{22}' => try!(write!(f, "\\\"")),
'\u{5c}' => try!(write!(f, "\\\\")),
ch => try!(write!(f, "{}", ch)),
}
}
write!(f, "\"")
}
impl<'a> fmt::Display for Key<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, part) in self.0.iter().enumerate() {
if i != 0 { try!(write!(f, ".")); }
let ok = part.chars().all(|c| {
match c {
'a' ... 'z' |
'A' ... 'Z' |
'0' ... '9' |
'-' | '_' => true,
_ => false,
}
});
if ok {
try!(write!(f, "{}", part));
} else {
try!(write_str(f, part));
}
}
Ok(())
}
}
#[test]
fn test_pretty() {
let mut examples = vec![
(r##"[example]
a_first = "hello world"
b_second = '''
this is a little longer
yay, it looks good!
'''
"##, None),
(r##"[a_first]
int = 7
long = '''
i like long text
it is nice
'''
[b_second]
int = 10
text = "this is some text"
"##, None),
(r##"[example]
b_second = ''' woot '''
a_first = "hello world"
"##,
Some(r##"[example]
a_first = "hello world"
b_second = " woot "
"##)),
("[b]\n[a]\n", Some("[a]\n\n[b]\n")),
];
use super::tests::parse_text;
for (i, (value, expected)) in examples.drain(..).enumerate() {
let expected = match expected {
Some(ref r) => r,
None => value,
};
assert_eq!((i, pretty_toml(parse_text(value)).unwrap()), (i, expected.to_string()));
}
}