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