Indented printing
This commit is contained in:
parent
af1405e87a
commit
3176e5f8d0
|
@ -15,7 +15,12 @@ pub enum CommaStyle {
|
|||
Terminating,
|
||||
}
|
||||
|
||||
pub struct TextWriter<W: io::Write>(Suspendable<W>, CommaStyle);
|
||||
pub struct TextWriter<W: io::Write> {
|
||||
w: Suspendable<W>,
|
||||
pub comma_style: CommaStyle,
|
||||
pub indentation: usize,
|
||||
indent: String,
|
||||
}
|
||||
|
||||
impl std::default::Default for CommaStyle {
|
||||
fn default() -> Self {
|
||||
|
@ -25,15 +30,20 @@ impl std::default::Default for CommaStyle {
|
|||
|
||||
impl<W: io::Write> TextWriter<W> {
|
||||
pub fn new(w: W) -> Self {
|
||||
TextWriter(Suspendable::new(w), CommaStyle::default())
|
||||
TextWriter {
|
||||
w: Suspendable::new(w),
|
||||
comma_style: CommaStyle::default(),
|
||||
indentation: 0,
|
||||
indent: "\n".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suspend(&mut self) -> Self {
|
||||
TextWriter(self.0.suspend(), self.1)
|
||||
TextWriter { w: self.w.suspend(), indent: self.indent.clone(), .. *self }
|
||||
}
|
||||
|
||||
pub fn resume(&mut self, other: Self) {
|
||||
self.0.resume(other.0)
|
||||
self.w.resume(other.w)
|
||||
}
|
||||
|
||||
pub fn write_stringlike_char_fallback<F>(
|
||||
|
@ -44,68 +54,109 @@ impl<W: io::Write> TextWriter<W> {
|
|||
F: FnOnce(&mut W, char) -> io::Result<()>
|
||||
{
|
||||
match c {
|
||||
'\\' => write!(self.0, "\\\\"),
|
||||
'\x08' => write!(self.0, "\\b"),
|
||||
'\x0c' => write!(self.0, "\\f"),
|
||||
'\x0a' => write!(self.0, "\\n"),
|
||||
'\x0d' => write!(self.0, "\\r"),
|
||||
'\x09' => write!(self.0, "\\t"),
|
||||
_ => f(&mut self.0, c),
|
||||
'\\' => write!(self.w, "\\\\"),
|
||||
'\x08' => write!(self.w, "\\b"),
|
||||
'\x0c' => write!(self.w, "\\f"),
|
||||
'\x0a' => write!(self.w, "\\n"),
|
||||
'\x0d' => write!(self.w, "\\r"),
|
||||
'\x09' => write!(self.w, "\\t"),
|
||||
_ => f(&mut self.w, c),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_stringlike_char(&mut self, c: char) -> io::Result<()> {
|
||||
self.write_stringlike_char_fallback(c, |w, c| write!(w, "{}", c))
|
||||
}
|
||||
|
||||
pub fn add_indent(&mut self) {
|
||||
for _ in 0 .. self.indentation {
|
||||
self.indent.push(' ')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn del_indent(&mut self) {
|
||||
if self.indentation > 0 {
|
||||
self.indent.truncate(self.indent.len() - self.indentation)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn indent(&mut self) -> io::Result<()> {
|
||||
if self.indentation > 0 {
|
||||
write!(self.w, "{}", &self.indent)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn indent_sp(&mut self) -> io::Result<()> {
|
||||
if self.indentation > 0 {
|
||||
write!(self.w, "{}", &self.indent)
|
||||
} else {
|
||||
write!(self.w, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> CompoundWriter for TextWriter<W> {
|
||||
fn boundary(&mut self, b: &B::Type) -> io::Result<()> {
|
||||
match (b.closing.as_ref(), b.opening.as_ref()) {
|
||||
(None, Some(B::Item::Annotation)) =>
|
||||
write!(self.0, "@"),
|
||||
(Some(_), Some(B::Item::Annotation)) =>
|
||||
write!(self.0, " @"),
|
||||
(Some(B::Item::Annotation), Some(B::Item::AnnotatedValue)) =>
|
||||
write!(self.0, " "),
|
||||
(None, Some(B::Item::RecordLabel)) |
|
||||
(Some(B::Item::RecordLabel), None) |
|
||||
(Some(B::Item::RecordField), None) =>
|
||||
return Ok(()),
|
||||
(_, Some(B::Item::RecordField)) =>
|
||||
return write!(self.w, " "),
|
||||
|
||||
(Some(B::Item::DictionaryKey), Some(B::Item::DictionaryValue)) =>
|
||||
write!(self.0, ": "),
|
||||
(Some(B::Item::DictionaryKey), Some(B::Item::DictionaryValue)) => {
|
||||
return write!(self.w, ": ")
|
||||
}
|
||||
|
||||
(Some(B::Item::RecordLabel), Some(B::Item::RecordField)) |
|
||||
(Some(B::Item::RecordField), Some(B::Item::RecordField)) =>
|
||||
write!(self.0, " "),
|
||||
(None, Some(B::Item::Annotation)) => {
|
||||
return write!(self.w, "@")
|
||||
}
|
||||
(Some(_), Some(B::Item::Annotation)) => {
|
||||
return write!(self.w, " @")
|
||||
}
|
||||
(Some(B::Item::Annotation), Some(B::Item::AnnotatedValue)) => {
|
||||
return write!(self.w, " ")
|
||||
}
|
||||
(Some(B::Item::AnnotatedValue), None) =>
|
||||
return Ok(()),
|
||||
|
||||
(Some(B::Item::DictionaryValue), Some(B::Item::DictionaryKey)) |
|
||||
(Some(B::Item::SequenceValue), Some(B::Item::SequenceValue)) |
|
||||
(Some(B::Item::SetValue), Some(B::Item::SetValue)) =>
|
||||
match self.1 {
|
||||
CommaStyle::Separating | CommaStyle::Terminating =>
|
||||
write!(self.0, ", "),
|
||||
CommaStyle::None =>
|
||||
write!(self.0, " "),
|
||||
},
|
||||
|
||||
(Some(B::Item::DictionaryValue), None) |
|
||||
(Some(B::Item::SequenceValue), None) |
|
||||
(Some(B::Item::SetValue), None) =>
|
||||
match self.1 {
|
||||
CommaStyle::Terminating =>
|
||||
write!(self.0, ","),
|
||||
CommaStyle::Separating | CommaStyle::None =>
|
||||
Ok(()),
|
||||
},
|
||||
|
||||
_ =>
|
||||
Ok(())
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match (b.closing.as_ref(), b.opening.as_ref()) {
|
||||
(None, None) => (),
|
||||
(None, Some(_)) => {
|
||||
self.add_indent();
|
||||
self.indent()?
|
||||
},
|
||||
(Some(_), Some(_)) => {
|
||||
match self.comma_style {
|
||||
CommaStyle::Separating | CommaStyle::Terminating => write!(self.w, ",")?,
|
||||
CommaStyle::None => (),
|
||||
}
|
||||
self.indent_sp()?
|
||||
}
|
||||
(Some(_), None) => {
|
||||
match self.comma_style {
|
||||
CommaStyle::Terminating => write!(self.w, ",")?,
|
||||
CommaStyle::Separating | CommaStyle::None => (),
|
||||
}
|
||||
self.del_indent();
|
||||
self.indent()?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! simple_writer_method {
|
||||
($n:ident, $argty:ty) =>
|
||||
(fn $n (&mut self, v: $argty) -> io::Result<()> {
|
||||
write!(self.0, "{}", v)
|
||||
write!(self.w, "{}", v)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -127,16 +178,16 @@ impl<W: io::Write> Writer for TextWriter<W> {
|
|||
}
|
||||
|
||||
fn write_bool(&mut self, v: bool) -> io::Result<()> {
|
||||
write!(self.0, "{}", if v { "#t" } else { "#f" })
|
||||
write!(self.w, "{}", if v { "#t" } else { "#f" })
|
||||
}
|
||||
|
||||
fn write_f32(&mut self, v: f32) -> io::Result<()> {
|
||||
dtoa::write(&mut *self.0, v)?;
|
||||
write!(self.0, "f")
|
||||
dtoa::write(&mut *self.w, v)?;
|
||||
write!(self.w, "f")
|
||||
}
|
||||
|
||||
fn write_f64(&mut self, v: f64) -> io::Result<()> {
|
||||
dtoa::write(&mut *self.0, v)?;
|
||||
dtoa::write(&mut *self.w, v)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -153,79 +204,79 @@ impl<W: io::Write> Writer for TextWriter<W> {
|
|||
simple_writer_method!(write_int, &BigInt);
|
||||
|
||||
fn write_string(&mut self, v: &str) -> io::Result<()> {
|
||||
write!(self.0, "\"")?;
|
||||
write!(self.w, "\"")?;
|
||||
for c in v.chars() {
|
||||
match c {
|
||||
'"' => write!(self.0, "\\\"")?,
|
||||
'"' => write!(self.w, "\\\"")?,
|
||||
_ => self.write_stringlike_char(c)?,
|
||||
}
|
||||
}
|
||||
write!(self.0, "\"")
|
||||
write!(self.w, "\"")
|
||||
}
|
||||
|
||||
fn write_bytes(&mut self, v: &[u8]) -> io::Result<()> {
|
||||
write!(self.0, "#[{}]", base64::encode_config(v, base64::STANDARD_NO_PAD))
|
||||
write!(self.w, "#[{}]", base64::encode_config(v, base64::STANDARD_NO_PAD))
|
||||
}
|
||||
|
||||
fn write_symbol(&mut self, v: &str) -> io::Result<()> {
|
||||
// FIXME: This regular expression is conservatively correct, but Anglo-chauvinistic.
|
||||
let re = regex::Regex::new("^[a-zA-Z~!$%^&*?_=+/.][-a-zA-Z~!$%^&*?_=+/.0-9]*$").unwrap();
|
||||
if re.is_match(v) {
|
||||
write!(self.0, "{}", v)
|
||||
write!(self.w, "{}", v)
|
||||
} else {
|
||||
write!(self.0, "|")?;
|
||||
write!(self.w, "|")?;
|
||||
for c in v.chars() {
|
||||
match c {
|
||||
'|' => write!(self.0, "\\|")?,
|
||||
'|' => write!(self.w, "\\|")?,
|
||||
_ => self.write_stringlike_char(c)?,
|
||||
}
|
||||
}
|
||||
write!(self.0, "|")
|
||||
write!(self.w, "|")
|
||||
}
|
||||
}
|
||||
|
||||
fn start_record(&mut self, _field_count: Option<usize>) -> io::Result<Self::RecWriter> {
|
||||
write!(self.0, "<")?;
|
||||
write!(self.w, "<")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_record(&mut self, rec: Self::RecWriter) -> io::Result<()> {
|
||||
self.resume(rec);
|
||||
write!(self.0, ">")
|
||||
write!(self.w, ">")
|
||||
}
|
||||
|
||||
fn start_sequence(&mut self, _item_count: Option<usize>) -> io::Result<Self::SeqWriter> {
|
||||
write!(self.0, "[")?;
|
||||
write!(self.w, "[")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_sequence(&mut self, seq: Self::SeqWriter) -> io::Result<()> {
|
||||
self.resume(seq);
|
||||
write!(self.0, "]")
|
||||
write!(self.w, "]")
|
||||
}
|
||||
|
||||
fn start_set(&mut self, _item_count: Option<usize>) -> io::Result<Self::SetWriter> {
|
||||
write!(self.0, "#{{")?;
|
||||
write!(self.w, "#{{")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_set(&mut self, set: Self::SetWriter) -> io::Result<()> {
|
||||
self.resume(set);
|
||||
write!(self.0, "}}")
|
||||
write!(self.w, "}}")
|
||||
}
|
||||
|
||||
fn start_dictionary(&mut self, _entry_count: Option<usize>) -> io::Result<Self::DictWriter> {
|
||||
write!(self.0, "{{")?;
|
||||
write!(self.w, "{{")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_dictionary(&mut self, dict: Self::DictWriter) -> io::Result<()> {
|
||||
self.resume(dict);
|
||||
write!(self.0, "}}")
|
||||
write!(self.w, "}}")
|
||||
}
|
||||
|
||||
fn start_embedded(&mut self) -> io::Result<Self::EmbeddedWriter> {
|
||||
write!(self.0, "#!")?;
|
||||
write!(self.w, "#!")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,16 @@ fn decode_all(bytes: &'_ [u8]) -> io::Result<Vec<IOValue>> {
|
|||
let s = String::from_utf8(bs).unwrap();
|
||||
preserves::value::text::annotated_iovalue_from_str(&s)?
|
||||
};
|
||||
let roundtripped_indented = {
|
||||
let mut bs = Vec::new();
|
||||
let mut w = preserves::value::TextWriter::new(&mut bs);
|
||||
w.indentation = 4;
|
||||
preserves::ser::to_writer(&mut w, &from_text)?;
|
||||
let s = String::from_utf8(bs).unwrap();
|
||||
preserves::value::text::annotated_iovalue_from_str(&s)?
|
||||
};
|
||||
assert_eq!(from_text, roundtripped);
|
||||
assert_eq!(from_text, roundtripped_indented);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue