Add basic emitter

This commit is contained in:
Yuheng Chen 2015-05-31 12:56:45 +08:00
parent fc1c15f225
commit 24d4fad5cf
3 changed files with 241 additions and 1 deletions

View file

@ -8,7 +8,7 @@ The missing YAML 1.2 implementation for Rust.
`yaml-rust` is a pure Rust YAML 1.2 implementation without
any FFI and crate dependencies, which enjoys the memory safe
property and other benefits from the Rust language.
The parser is havily influenced by `libyaml` and `yaml-cpp`.
The parser is heavily influenced by `libyaml` and `yaml-cpp`.
NOTE: This library is still under heavily development.

239
saphyr/src/emitter.rs Normal file
View file

@ -0,0 +1,239 @@
use std::fmt;
use std::convert::From;
use yaml::*;
#[derive(Copy, Clone, Debug)]
pub enum EmitError {
FmtError(fmt::Error),
BadHashmapKey,
}
impl From<fmt::Error> for EmitError {
fn from(f: fmt::Error) -> Self {
EmitError::FmtError(f)
}
}
pub struct YamlEmitter<'a> {
writer: &'a mut fmt::Write,
best_indent: usize,
level: isize,
}
pub type EmitResult = Result<(), EmitError>;
// from serialize::json
fn escape_str(wr: &mut fmt::Write, v: &str) -> Result<(), fmt::Error> {
try!(wr.write_str("\""));
let mut start = 0;
for (i, byte) in v.bytes().enumerate() {
let escaped = match byte {
b'"' => "\\\"",
b'\\' => "\\\\",
b'\x00' => "\\u0000",
b'\x01' => "\\u0001",
b'\x02' => "\\u0002",
b'\x03' => "\\u0003",
b'\x04' => "\\u0004",
b'\x05' => "\\u0005",
b'\x06' => "\\u0006",
b'\x07' => "\\u0007",
b'\x08' => "\\b",
b'\t' => "\\t",
b'\n' => "\\n",
b'\x0b' => "\\u000b",
b'\x0c' => "\\f",
b'\r' => "\\r",
b'\x0e' => "\\u000e",
b'\x0f' => "\\u000f",
b'\x10' => "\\u0010",
b'\x11' => "\\u0011",
b'\x12' => "\\u0012",
b'\x13' => "\\u0013",
b'\x14' => "\\u0014",
b'\x15' => "\\u0015",
b'\x16' => "\\u0016",
b'\x17' => "\\u0017",
b'\x18' => "\\u0018",
b'\x19' => "\\u0019",
b'\x1a' => "\\u001a",
b'\x1b' => "\\u001b",
b'\x1c' => "\\u001c",
b'\x1d' => "\\u001d",
b'\x1e' => "\\u001e",
b'\x1f' => "\\u001f",
b'\x7f' => "\\u007f",
_ => { continue; }
};
if start < i {
try!(wr.write_str(&v[start..i]));
}
try!(wr.write_str(escaped));
start = i + 1;
}
if start != v.len() {
try!(wr.write_str(&v[start..]));
}
try!(wr.write_str("\""));
Ok(())
}
impl<'a> YamlEmitter<'a> {
pub fn new(writer: &'a mut fmt::Write) -> YamlEmitter {
YamlEmitter {
writer: writer,
best_indent: 2,
level: -1
}
}
pub fn dump(&mut self, doc: &Yaml) -> EmitResult {
// write DocumentStart
try!(write!(self.writer, "---\n"));
self.level = -1;
self.emit_node(doc)
}
fn write_indent(&mut self) -> EmitResult {
if self.level <= 0 { return Ok(()); }
for _ in 0..self.level {
for _ in 0..self.best_indent {
try!(write!(self.writer, " "));
}
}
Ok(())
}
fn emit_node(&mut self, node: &Yaml) -> EmitResult {
match node {
&Yaml::Array(ref v) => {
if v.is_empty() {
try!(write!(self.writer, "[]"));
Ok(())
} else {
if self.level >= 0 {
try!(write!(self.writer, "\n"));
}
self.level += 1;
let mut cnt = 0usize;
for x in v {
try!(self.write_indent());
try!(write!(self.writer, "- "));
try!(self.emit_node(x));
cnt += 1;
if cnt < v.len() {
try!(write!(self.writer, "\n"));
}
}
self.level -= 1;
Ok(())
}
},
&Yaml::Hash(ref h) => {
if h.is_empty() {
try!(self.writer.write_str("{}"));
Ok(())
} else {
if self.level >= 0 {
try!(write!(self.writer, "\n"));
}
self.level += 1;
let mut cnt = 0usize;
for (k, v) in h {
try!(self.write_indent());
match k {
// complex key is not supported
&Yaml::Array(_) | &Yaml::Hash(_) => {
return Err(EmitError::BadHashmapKey);
},
_ => { try!(self.emit_node(k)); }
}
try!(write!(self.writer, ": "));
try!(self.emit_node(v));
cnt += 1;
if cnt < h.len() {
try!(write!(self.writer, "\n"));
}
}
self.level -= 1;
Ok(())
}
},
&Yaml::String(ref v) => {
try!(escape_str(self.writer, v));
Ok(())
},
&Yaml::Boolean(v) => {
if v {
try!(self.writer.write_str("true"));
} else {
try!(self.writer.write_str("false"));
}
Ok(())
},
&Yaml::Integer(v) => {
try!(write!(self.writer, "{}", v));
Ok(())
},
&Yaml::Real(ref v) => {
try!(write!(self.writer, "{}", v));
Ok(())
},
&Yaml::Null | &Yaml::BadValue => {
try!(write!(self.writer, "~"));
Ok(())
},
// XXX(chenyh) Alias
_ => { Ok(()) }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use yaml::*;
#[test]
fn test_emit_simple() {
let s = "
# comment
a0 bb: val
a1:
b1: 4
b2: d
a2: 4 # i'm comment
a3: [1, 2, 3]
a4:
- - a1
- a2
- 2
a5: 'single_quoted'
a6: \"double_quoted\"
a7:
'key 1': \"ddd\\\tbbb\"
";
let docs = YamlLoader::load_from_str(&s).unwrap();
let doc = &docs[0];
let mut writer = String::new();
{
let mut emitter = YamlEmitter::new(&mut writer);
emitter.dump(doc).unwrap();
}
let docs_new = YamlLoader::load_from_str(&s).unwrap();
let doc_new = &docs_new[0];
println!("{}", writer);
assert_eq!(doc, doc_new);
}
}

View file

@ -1,6 +1,7 @@
pub mod yaml;
pub mod scanner;
pub mod parser;
pub mod emitter;
#[test]
fn it_works() {