From 52de025c21da33f490471cc088c93ae6457bfec8 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Wed, 26 Oct 2022 16:00:48 +0200 Subject: [PATCH] Parsing and reading via TextReader/TextWriter --- .../rust/preserves/examples/extensibility.rs | 2 +- implementations/rust/preserves/src/lib.rs | 53 ++++++++++++ .../rust/preserves/src/value/domain.rs | 17 ++++ .../rust/preserves/src/value/mod.rs | 2 + .../rust/preserves/src/value/repr.rs | 83 +++++++++---------- .../rust/preserves/src/value/text/writer.rs | 15 +++- 6 files changed, 128 insertions(+), 44 deletions(-) diff --git a/implementations/rust/preserves/examples/extensibility.rs b/implementations/rust/preserves/examples/extensibility.rs index a0a0e41..6df3376 100644 --- a/implementations/rust/preserves/examples/extensibility.rs +++ b/implementations/rust/preserves/examples/extensibility.rs @@ -34,7 +34,7 @@ enum Variety { fn try_file(kind: &str, path: &str) -> io::Result<()> { let fruits_value = IOBinarySource::new(&mut File::open(path)?).packed_iovalues().demand_next(true)?; - println!("{:?}", fruits_value); + println!("{:#?}", fruits_value); let fruits1: Vec = value::de::from_value(&fruits_value)?; println!("(via generic decoding) {}: {:?}", kind, fruits1); diff --git a/implementations/rust/preserves/src/lib.rs b/implementations/rust/preserves/src/lib.rs index fa305b3..124fb19 100644 --- a/implementations/rust/preserves/src/lib.rs +++ b/implementations/rust/preserves/src/lib.rs @@ -19,6 +19,17 @@ mod dom { impl Domain for Dom {} + impl std::str::FromStr for Dom { + type Err = io::Error; + fn from_str(s: &str) -> Result { + match s { + "One" => Ok(Dom::One), + "Two" => Ok(Dom::Two), + _ => Err(io::Error::new(io::ErrorKind::Other, "cannot parse preserves test domain")), + } + } + } + fn dom_as_preserves(v: &Dom) -> io::Result { Ok(match v { Dom::One => Value::bytestring(vec![255, 255, 255, 255]), @@ -366,6 +377,48 @@ mod decoder_tests { } } +#[cfg(test)] +mod formatting_tests { + use super::dom::Dom; + use super::value::ArcValue; + use super::value::IOValue; + use super::value::Value; + + #[test] fn format_debug_and_parse() { + let v = "[1, {z: 2, a: #!\"One\"}, 3]".parse::>>().unwrap(); + assert_eq!(format!("{:?}", &v), "[1, {a: #!\"One\", z: 2}, 3]"); + } + + #[test] fn format_pretty_debug_and_parse() { + let v = "[1, {z: 2, a: #!\"One\"}, 3]".parse::>>().unwrap(); + assert_eq!(format!("{:#?}", &v), concat!( + "[\n", + " 1,\n", + " {\n", + " a: #!\"One\",\n", + " z: 2\n", + " },\n", + " 3\n", + "]")); + } + + #[test] fn iovalue_parse() { + let v = "[1 @{a:b c:d} @\"foo\" #![2 3] 4]".parse::().unwrap(); + assert_eq!(format!("{:#?}", &v), concat!( + "[\n", + " 1,\n", + " @{\n", + " a: b,\n", + " c: d\n", + " } @\"foo\" #![\n", + " 2,\n", + " 3\n", + " ],\n", + " 4\n", + "]")); + } +} + #[cfg(test)] mod serde_tests { use crate::symbol::Symbol; diff --git a/implementations/rust/preserves/src/value/domain.rs b/implementations/rust/preserves/src/value/domain.rs index 7c8c32a..4e5967b 100644 --- a/implementations/rust/preserves/src/value/domain.rs +++ b/implementations/rust/preserves/src/value/domain.rs @@ -4,6 +4,7 @@ use super::BinarySource; use super::BytesBinarySource; use super::Embeddable; use super::IOValue; +use super::NestedValue; use super::Reader; use super::Writer; use super::packed; @@ -50,6 +51,22 @@ impl<'a, D: Embeddable, T: DomainDecode> DomainDecode for &'a mut T { } } +pub struct DebugDomainEncode; + +impl DomainEncode for DebugDomainEncode { + fn encode_embedded(&mut self, w: &mut W, d: &D) -> io::Result<()> { + d.debug_encode(w) + } +} + +pub struct FromStrDomainParse; + +impl, D: Embeddable + std::str::FromStr> DomainParse for FromStrDomainParse { + fn parse_embedded(&mut self, v: &IOValue) -> io::Result { + Ok(D::from_str(v.value().to_string()?).map_err(|e| e.into())?) + } +} + pub struct IOValueDomainCodec; impl DomainDecode for IOValueDomainCodec { diff --git a/implementations/rust/preserves/src/value/mod.rs b/implementations/rust/preserves/src/value/mod.rs index 1ae56b4..77ace7f 100644 --- a/implementations/rust/preserves/src/value/mod.rs +++ b/implementations/rust/preserves/src/value/mod.rs @@ -17,6 +17,8 @@ pub use de::from_value; pub use domain::DomainDecode; pub use domain::DomainEncode; pub use domain::DomainParse; +pub use domain::DebugDomainEncode; +pub use domain::FromStrDomainParse; pub use domain::IOValueDomainCodec; pub use domain::NoEmbeddedDomainCodec; pub use domain::ViaCodec; diff --git a/implementations/rust/preserves/src/value/repr.rs b/implementations/rust/preserves/src/value/repr.rs index 9bdbe67..633f6e3 100644 --- a/implementations/rust/preserves/src/value/repr.rs +++ b/implementations/rust/preserves/src/value/repr.rs @@ -7,6 +7,7 @@ use std::convert::TryFrom; use std::convert::TryInto; use std::fmt::Debug; use std::hash::{Hash, Hasher}; +use std::io; use std::ops::Index; use std::ops::IndexMut; use std::string::String; @@ -16,14 +17,29 @@ use std::vec::Vec; pub use std::collections::BTreeSet as Set; pub use std::collections::BTreeMap as Map; +use super::DebugDomainEncode; +use super::FromStrDomainParse; +use super::IOValueDomainCodec; +use super::TextWriter; +use super::Writer; use super::signed_integer::SignedInteger; +use super::text; use crate::error::{Error, ExpectedKind, Received}; -pub trait Domain: Sized + Debug + Eq + Hash + Ord {} +pub trait Domain: Sized + Debug + Eq + Hash + Ord { + fn debug_encode(&self, w: &mut W) -> io::Result<()> { + w.write_string(&format!("{:?}", self)) + } +} + pub trait Embeddable: Domain + Clone {} impl Embeddable for T where T: Domain + Clone {} -impl Domain for Arc {} +impl Domain for Arc { + fn debug_encode(&self, w: &mut W) -> io::Result<()> { + self.as_ref().debug_encode(w) + } +} pub trait NestedValue: Sized + Debug + Clone + Eq + Hash + Ord { type Embedded: Embeddable; @@ -312,46 +328,18 @@ impl From> for Value { fn from(v: Set) -> Self { Va impl From> for Value { fn from(v: Map) -> Self { Value::Dictionary(v) } } impl Debug for Value { - // Not *quite* a formatter for the Preserves text syntax, since it - // doesn't escape strings/symbols properly. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Value::Boolean(false) => f.write_str("#f"), - Value::Boolean(true) => f.write_str("#t"), - Value::Float(Float(v)) => write!(f, "{:?}f", v), - Value::Double(Double(v)) => write!(f, "{:?}", v), - Value::SignedInteger(v) => write!(f, "{}", v), - Value::String(v) => write!(f, "{:?}", v), // TODO: proper escaping! - Value::ByteString(v) => { - f.write_str("#x\"")?; - for b in v { write!(f, "{:02x}", b)? } - f.write_str("\"") - } - Value::Symbol(v) => { - // TODO: proper escaping! - if v.is_empty() { - f.write_str("||") - } else { - write!(f, "{}", v) - } - } - Value::Record(r) => { - f.write_str("<")?; - r.label().fmt(f)?; - for v in r.fields() { - f.write_str(" ")?; - v.fmt(f)?; - } - f.write_str(">") - } - Value::Sequence(v) => v.fmt(f), - Value::Set(v) => { - f.write_str("#")?; - f.debug_set().entries(v.iter()).finish() - } - Value::Dictionary(v) => f.debug_map().entries(v.iter()).finish(), - Value::Embedded(d) => write!(f, "#!{:?}", d), - } + TextWriter::fmt_value(f, &mut DebugDomainEncode, self).map_err(|_| std::fmt::Error) + } +} + +impl, D: Embeddable + std::str::FromStr, N: NestedValue> + std::str::FromStr for Value +{ + type Err = io::Error; + + fn from_str(s: &str) -> Result { + Ok(text::from_str::(s, FromStrDomainParse)?.value_owned()) } } @@ -1432,7 +1420,11 @@ impl Debug for ArcValue { pub struct IOValue(Arc>); pub type UnwrappedIOValue = Value; -impl Domain for IOValue {} +impl Domain for IOValue { + fn debug_encode(&self, w: &mut W) -> io::Result<()> { + w.write(&mut IOValueDomainCodec, self) + } +} impl NestedValue for IOValue { type Embedded = Self; @@ -1475,6 +1467,13 @@ impl Debug for IOValue { } } +impl std::str::FromStr for IOValue { + type Err = io::Error; + fn from_str(s: &str) -> Result { + text::annotated_iovalue_from_str(s) + } +} + impl serde::Serialize for IOValue { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { super::magic::output_value(serializer, self.clone()) diff --git a/implementations/rust/preserves/src/value/text/writer.rs b/implementations/rust/preserves/src/value/text/writer.rs index 4643dca..3eb8446 100644 --- a/implementations/rust/preserves/src/value/text/writer.rs +++ b/implementations/rust/preserves/src/value/text/writer.rs @@ -29,11 +29,24 @@ pub struct TextWriter { impl std::default::Default for CommaStyle { fn default() -> Self { - CommaStyle::None + CommaStyle::Separating } } impl TextWriter<&mut Vec> { + pub fn fmt_value>( + f: &mut std::fmt::Formatter<'_>, + enc: &mut Enc, + v: &crate::value::Value, + ) -> io::Result<()> { + let mut buf: Vec = Vec::new(); + let mut w = TextWriter::new(&mut buf); + if f.alternate() { w.indentation = 4 } + w.write_value(enc, v)?; + f.write_str(std::str::from_utf8(&buf).expect("valid UTF-8 from TextWriter")).map_err( + |_| io::Error::new(io::ErrorKind::Other, "could not append to Formatter")) + } + pub fn encode>( enc: &mut Enc, v: &N,