toml-rs/src/display.rs

203 lines
6.4 KiB
Rust
Raw Normal View History

use std::fmt;
2014-12-06 16:51:51 -06:00
use Table as TomlTable;
use Value::{self, String, Integer, Float, Boolean, Datetime, Array, Table};
struct Printer<'a, 'b:'a> {
output: &'a mut fmt::Formatter<'b>,
stack: Vec<&'a str>,
}
struct Key<'a>(&'a [&'a str]);
2015-01-23 10:19:29 -06:00
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
String(ref s) => write_str(f, s),
Integer(i) => write!(f, "{}", i),
Float(fp) => {
try!(write!(f, "{}", fp));
if fp % 1.0 == 0.0 { try!(write!(f, ".0")) }
Ok(())
}
Boolean(b) => write!(f, "{}", b),
Datetime(ref s) => write!(f, "{}", s),
Table(ref t) => {
let mut p = Printer { output: f, stack: Vec::new() };
p.print(t)
}
Array(ref a) => {
2015-01-09 13:48:06 -06:00
try!(write!(f, "["));
for (i, v) in a.iter().enumerate() {
if i != 0 { try!(write!(f, ", ")); }
try!(write!(f, "{}", v));
}
write!(f, "]")
}
}
}
}
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, 'b> Printer<'a, 'b> {
2014-09-21 09:55:13 -05:00
fn print(&mut self, table: &'a TomlTable) -> fmt::Result {
for (k, v) in table.iter() {
match *v {
Table(..) => continue,
Array(ref a) => {
match a.first() {
Some(&Table(..)) => continue,
_ => {}
}
}
_ => {}
}
try!(writeln!(self.output, "{} = {}", Key(&[k]), v));
}
for (k, v) in table.iter() {
match *v {
Table(ref inner) => {
self.stack.push(k);
try!(writeln!(self.output, "\n[{}]", Key(&self.stack)));
try!(self.print(inner));
self.stack.pop();
}
Array(ref inner) => {
match inner.first() {
Some(&Table(..)) => {}
_ => continue
}
self.stack.push(k);
for inner in inner.iter() {
try!(writeln!(self.output, "\n[[{}]]", Key(&self.stack)));
match *inner {
Table(ref inner) => try!(self.print(inner)),
2014-10-29 20:45:18 -05:00
_ => panic!("non-heterogeneous toml array"),
}
}
self.stack.pop();
}
_ => {},
}
}
Ok(())
}
}
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(())
}
}
#[cfg(test)]
#[allow(warnings)]
mod tests {
2014-11-18 02:13:21 -06:00
use Value;
use Value::{String, Integer, Float, Boolean, Datetime, Array, Table};
2014-12-21 00:35:14 -06:00
use std::collections::BTreeMap;
2015-01-09 13:48:06 -06:00
macro_rules! map( ($($k:expr => $v:expr),*) => ({
2014-12-21 00:35:14 -06:00
let mut _m = BTreeMap::new();
2014-07-09 08:35:54 -05:00
$(_m.insert($k.to_string(), $v);)*
_m
}) );
#[test]
fn simple_show() {
2015-03-09 13:09:07 -05:00
assert_eq!(String("foo".to_string()).to_string(),
"\"foo\"");
2015-03-09 13:09:07 -05:00
assert_eq!(Integer(10).to_string(),
"10");
2015-03-09 13:09:07 -05:00
assert_eq!(Float(10.0).to_string(),
"10.0");
2015-03-09 13:09:07 -05:00
assert_eq!(Float(2.4).to_string(),
"2.4");
2015-03-09 13:09:07 -05:00
assert_eq!(Boolean(true).to_string(),
"true");
2015-03-09 13:09:07 -05:00
assert_eq!(Datetime("test".to_string()).to_string(),
"test");
2015-03-09 13:09:07 -05:00
assert_eq!(Array(vec![]).to_string(),
"[]");
2015-03-09 13:09:07 -05:00
assert_eq!(Array(vec![Integer(1), Integer(2)]).to_string(),
"[1, 2]");
}
#[test]
fn table() {
2015-03-09 13:09:07 -05:00
assert_eq!(Table(map! { }).to_string(),
"");
2015-03-09 13:09:07 -05:00
assert_eq!(Table(map! { "test" => Integer(2) }).to_string(),
"test = 2\n");
assert_eq!(Table(map! {
2015-01-09 13:48:06 -06:00
"test" => Integer(2),
"test2" => Table(map! {
"test" => String("wut".to_string())
})
2015-03-09 13:09:07 -05:00
}).to_string(),
"test = 2\n\
\n\
[test2]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
2015-01-09 13:48:06 -06:00
"test" => Integer(2),
"test2" => Table(map! {
"test" => String("wut".to_string())
})
2015-03-09 13:09:07 -05:00
}).to_string(),
"test = 2\n\
\n\
[test2]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
2015-01-09 13:48:06 -06:00
"test" => Integer(2),
"test2" => Array(vec![Table(map! {
"test" => String("wut".to_string())
})])
2015-03-09 13:09:07 -05:00
}).to_string(),
"test = 2\n\
\n\
[[test2]]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
"foo.bar" => Integer(2),
"foo\"bar" => Integer(2)
2015-03-09 13:09:07 -05:00
}).to_string(),
"\"foo\\\"bar\" = 2\n\
\"foo.bar\" = 2\n");
}
}