hipack/
writer.rs

1// Copyright 2015-2016 hipack-rs developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::io;
10use std::collections::BTreeMap;
11use super::value::Value;
12
13
14pub trait Formatter {
15    fn start_compound<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
16        where W: io::Write;
17    fn end_compound<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
18        where W: io::Write;
19    fn key_separator<W>(&mut self, writer: &mut W, next: &Value) -> io::Result<()>
20        where W: io::Write;
21    fn item_separator<W>(&mut self, writer: &mut W, next: &Value) -> io::Result<()>
22        where W: io::Write;
23}
24
25
26pub struct CompactFormatter;
27
28impl Formatter for CompactFormatter {
29    fn start_compound<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
30        where W: io::Write
31    {
32        writer.write_all(&[ch])
33    }
34
35    fn end_compound<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
36        where W: io::Write
37    {
38        writer.write_all(&[ch])
39    }
40
41    fn key_separator<W>(&mut self, writer: &mut W, next: &Value) -> io::Result<()>
42        where W: io::Write
43    {
44        match *next {
45            Value::Dict(_) | Value::List(_) => Ok(()),
46            _ => writer.write_all(b":"),
47        }
48    }
49
50    fn item_separator<W>(&mut self, writer: &mut W, next: &Value) -> io::Result<()>
51        where W: io::Write
52    {
53        match *next {
54            Value::Dict(_) | Value::List(_) => Ok(()),
55            _ => writer.write_all(b","),
56        }
57    }
58}
59
60
61pub struct PrettyFormatter {
62    indent: usize,
63}
64
65impl PrettyFormatter {
66    fn new() -> Self {
67        PrettyFormatter { indent: 0 }
68    }
69}
70
71#[inline]
72fn indent<W>(writer: &mut W, indent: usize) -> io::Result<()>
73    where W: io::Write
74{
75    for _ in 0..indent {
76        try!(writer.write_all(b"  "));
77    }
78    Ok(())
79}
80
81impl Formatter for PrettyFormatter {
82    fn start_compound<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
83        where W: io::Write
84    {
85        self.indent += 1;
86        try!(writer.write_all(&[ch, b'\n']));
87        indent(writer, self.indent)
88    }
89
90    fn end_compound<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
91        where W: io::Write
92    {
93        self.indent -= 1;
94        try!(writer.write(b"\n"));
95        try!(indent(writer, self.indent));
96        writer.write_all(&[ch])
97    }
98
99    fn key_separator<W>(&mut self, writer: &mut W, next: &Value) -> io::Result<()>
100        where W: io::Write
101    {
102        match *next {
103            Value::Dict(_) | Value::List(_) => writer.write_all(b" "),
104            _ => writer.write_all(b": "),
105        }
106    }
107
108    fn item_separator<W>(&mut self, writer: &mut W, _next: &Value) -> io::Result<()>
109        where W: io::Write
110    {
111        try!(writer.write(b"\n"));
112        indent(writer, self.indent)
113    }
114}
115
116
117pub struct Writer<W, F=PrettyFormatter> {
118    writer: W,
119    format: F,
120}
121
122
123impl<W> Writer<W, CompactFormatter>
124    where W: io::Write
125{
126    #[inline]
127    pub fn compact(writer: W) -> Self {
128        Writer::with_formatter(writer, CompactFormatter)
129    }
130}
131
132impl<W> Writer<W>
133    where W: io::Write
134{
135    #[inline]
136    pub fn pretty(writer: W) -> Self {
137        Writer::with_formatter(writer, PrettyFormatter::new())
138    }
139}
140
141impl<W, F> Writer<W, F>
142    where W: io::Write,
143          F: Formatter,
144{
145    #[inline]
146    fn with_formatter(writer: W, formatter: F) -> Self
147        where W: io::Write,
148              F: Formatter,
149    {
150        Writer {
151            writer: writer,
152            format: formatter,
153        }
154    }
155
156    fn write_value(&mut self, value: &Value) -> io::Result<()> {
157        match *value {
158            Value::Bool(value) => self.write_bool(value),
159            Value::Float(value) => self.write_float(value),
160            Value::Integer(value) => self.write_integer(value),
161            Value::String(ref value) => self.write_string(value),
162            Value::List(ref value) => self.write_list(value),
163            Value::Dict(ref value) => self.write_dict(value),
164        }
165    }
166
167    #[inline]
168    fn write_bool(&mut self, value: bool) -> io::Result<()> {
169        if value {
170            self.writer.write_all(b"True")
171        } else {
172            self.writer.write_all(b"False")
173        }
174    }
175
176    #[inline]
177    fn write_float(&mut self, value: f64) -> io::Result<()> {
178        if value.is_nan() || value.is_infinite() {
179            write!(self.writer, "{}", value)
180        } else {
181            let s = format!("{}", value);
182            try!(self.writer.write_all(s.as_bytes()));
183            if !s.contains(".") {
184                try!(self.writer.write_all(b".0"));
185            }
186            Ok(())
187        }
188    }
189
190    #[inline]
191    fn write_integer(&mut self, value: i64) -> io::Result<()> {
192        write!(self.writer, "{}", value)
193    }
194
195    #[inline]
196    fn write_string(&mut self, value: &String) -> io::Result<()> {
197        try!(self.writer.write_all(b"\""));
198        for ch in value.bytes() {
199            try!(match ch {
200                0x09 => self.writer.write_all(b"\\t"),
201                0x0A => self.writer.write_all(b"\\n"),
202                0x0D => self.writer.write_all(b"\\r"),
203                0x22 => self.writer.write_all(b"\\\""),
204                0x5C => self.writer.write_all(b"\\\\"),
205                ch if ch < 0xF => write!(self.writer, "\\0{:X}", ch),
206                ch if ch < 0x20 => write!(self.writer, "\\{:X}", ch),
207                ch => self.writer.write_all(&[ch]),
208            });
209        }
210        self.writer.write_all(b"\"")
211    }
212
213    fn write_list(&mut self, value: &Vec<Value>) -> io::Result<()> {
214        if value.is_empty() {
215            return self.writer.write_all(b"[]");
216        }
217
218        let mut iter = value.iter().peekable();
219        try!(self.format.start_compound(&mut self.writer, b'['));
220        loop {
221            match iter.next() {
222                None => break,
223                Some(value) => {
224                    try!(self.write_value(value));
225                    match iter.peek () {
226                        Some(_) =>
227                            try!(self.format.item_separator(&mut self.writer, value)),
228                        None => (),
229                    }
230                },
231            }
232        }
233        self.format.end_compound(&mut self.writer, b']')
234    }
235
236    #[inline]
237    fn write_dict(&mut self, dict: &BTreeMap<String, Value>) -> io::Result<()> {
238        if dict.is_empty() {
239            return self.writer.write_all(b"{}");
240        }
241
242        try!(self.format.start_compound(&mut self.writer, b'{'));
243        try!(self.write_keyval_items(dict));
244        self.format.end_compound(&mut self.writer, b'}')
245    }
246
247    fn write_keyval_items(&mut self, dict: &BTreeMap<String, Value>) -> io::Result<()> {
248        let mut iter = dict.iter().peekable();
249        loop {
250            match iter.next() {
251                None => break,
252                Some((key, value)) => {
253                    try!(self.writer.write_all(key.as_bytes()));
254                    try!(self.format.key_separator(&mut self.writer, value));
255                    try!(self.write_value(value));
256                    match iter.peek () {
257                        Some(&(_, ref value)) =>
258                            try!(self.format.item_separator(&mut self.writer, value)),
259                        None => (),
260                    }
261                },
262            }
263        }
264        Ok(())
265    }
266
267    #[inline]
268    pub fn write_message(&mut self, value: &BTreeMap<String, Value>) -> io::Result<()> {
269        self.write_keyval_items(value)
270    }
271
272    #[inline]
273    pub fn write_framed_message(&mut self, value: &BTreeMap<String, Value>) -> io::Result<()> {
274        self.write_dict(value)
275    }
276}
277
278
279#[cfg(test)]
280mod tests {
281	use super::*;
282    use super::super::value::Value;
283    use std::io::Cursor;
284    use std::collections::BTreeMap;
285    use std::f64::{NAN, INFINITY};
286
287    macro_rules! make_write_test {
288        ($name:ident, $value:expr, $pretty:expr, $compact:expr) => {
289            #[test]
290            fn $name() {
291                {
292                    println!("Pretty");
293                    let mut output = Cursor::new(Vec::new());
294                    Writer::pretty(&mut output).write_value($value).ok();
295                    assert_eq!($pretty, String::from_utf8(output.into_inner()).unwrap());
296                }
297                {
298                    println!("Compact");
299                    let mut output = Cursor::new(Vec::new());
300                    Writer::compact(&mut output).write_value($value).ok();
301                    assert_eq!($compact, String::from_utf8(output.into_inner()).unwrap());
302                }
303            }
304        }
305    }
306
307    make_write_test!(bool_true,  &Value::Bool(true),  "True", "True");
308    make_write_test!(bool_false, &Value::Bool(false), "False", "False");
309
310    make_write_test!(list_empty, &Value::List(vec![]), "[]", "[]");
311    make_write_test!(list_one,   &Value::List(vec![Value::Bool(true)]), "[\n  True\n]", "[True]");
312    make_write_test!(list_two,   &Value::List(vec![Value::Bool(true), Value::Bool(false)]),
313        "[\n  True\n  False\n]", "[True,False]");
314    make_write_test!(list_nested,
315                     &Value::List(vec![
316                                  Value::Bool(true),
317                                  Value::List(vec![Value::Bool(false)])]),
318                     "[\n  True\n  [\n    False\n  ]\n]", "[True,[False]]");
319
320    make_write_test!(dict_empty, &Value::Dict(BTreeMap::new()), "{}", "{}");
321    make_write_test!(dict_one,   &Value::Dict((|| {
322        let mut b = BTreeMap::new();
323        b.insert("item".to_string(), Value::Bool(true));
324        b
325    })()), "{\n  item: True\n}", "{item:True}");
326    make_write_test!(dict_two,   &Value::Dict((|| {
327        let mut b = BTreeMap::new();
328        b.insert("foo".to_string(), Value::Bool(true));
329        b.insert("bar".to_string(), Value::Bool(false));
330        b
331    })()), "{\n  bar: False\n  foo: True\n}", "{bar:False,foo:True}");
332
333    macro_rules! make_write_string_tests {
334        ($($name:ident, $value:expr, $expected:expr),+) => {
335            $( make_write_test!($name, &Value::String($value.to_string()), $expected, $expected); )*
336        }
337    }
338
339    make_write_string_tests!(string_empty, "", "\"\"",
340                             string_non_empty, "foo bar", "\"foo bar\"",
341                             string_unicode, "☺", "\"☺\"",
342                             string_escapes, "\n\r\t\\\"", "\"\\n\\r\\t\\\\\\\"\"",
343                             string_hexcode, "\0", "\"\\00\"");
344
345
346    macro_rules! make_write_number_tests {
347        ($t:ident, $($name:ident, $value:expr, $expected:expr),+) => {
348            $( make_write_test!($name, &Value::$t($value), $expected, $expected); )*
349        }
350    }
351
352    make_write_number_tests!(Integer,
353                             integer_zero, 0, "0",
354                             integer_negative, -34, "-34");
355    make_write_number_tests!(Float,
356                             float_zero, 0.0, "0.0",
357                             float_suffix, 1f64, "1.0",
358                             float_positive, 4.5, "4.5",
359                             float_negative, -3.2, "-3.2",
360                             float_nan, NAN, "NaN",
361                             float_infinite, INFINITY, "inf");
362}