1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
use std::fmt;

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]);

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) => {
                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> {
    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)),
                            _ => 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 {
    use Value;
    use Value::{String, Integer, Float, Boolean, Datetime, Array, Table};
    use std::collections::BTreeMap;

    macro_rules! map( ($($k:expr => $v:expr),*) => ({
        let mut _m = BTreeMap::new();
        $(_m.insert($k.to_string(), $v);)*
        _m
    }) );

    #[test]
    fn simple_show() {
        assert_eq!(String("foo".to_string()).to_string(),
                   "\"foo\"");
        assert_eq!(Integer(10).to_string(),
                   "10");
        assert_eq!(Float(10.0).to_string(),
                   "10.0");
        assert_eq!(Float(2.4).to_string(),
                   "2.4");
        assert_eq!(Boolean(true).to_string(),
                   "true");
        assert_eq!(Datetime("test".to_string()).to_string(),
                   "test");
        assert_eq!(Array(vec![]).to_string(),
                   "[]");
        assert_eq!(Array(vec![Integer(1), Integer(2)]).to_string(),
                   "[1, 2]");
    }

    #[test]
    fn table() {
        assert_eq!(Table(map! { }).to_string(),
                   "");
        assert_eq!(Table(map! { "test" => Integer(2) }).to_string(),
                   "test = 2\n");
        assert_eq!(Table(map! {
                        "test" => Integer(2),
                        "test2" => Table(map! {
                            "test" => String("wut".to_string())
                        })
                   }).to_string(),
                   "test = 2\n\
                    \n\
                    [test2]\n\
                    test = \"wut\"\n");
        assert_eq!(Table(map! {
                        "test" => Integer(2),
                        "test2" => Table(map! {
                            "test" => String("wut".to_string())
                        })
                   }).to_string(),
                   "test = 2\n\
                    \n\
                    [test2]\n\
                    test = \"wut\"\n");
        assert_eq!(Table(map! {
                        "test" => Integer(2),
                        "test2" => Array(vec![Table(map! {
                            "test" => String("wut".to_string())
                        })])
                   }).to_string(),
                   "test = 2\n\
                    \n\
                    [[test2]]\n\
                    test = \"wut\"\n");
        assert_eq!(Table(map! {
                        "foo.bar" => Integer(2),
                        "foo\"bar" => Integer(2)
                   }).to_string(),
                   "\"foo\\\"bar\" = 2\n\
                    \"foo.bar\" = 2\n");
    }
}