Parsing and reading via TextReader/TextWriter

This commit is contained in:
Tony Garnock-Jones 2022-10-26 16:00:48 +02:00
parent a1043f406c
commit 52de025c21
6 changed files with 128 additions and 44 deletions

View File

@ -34,7 +34,7 @@ enum Variety {
fn try_file(kind: &str, path: &str) -> io::Result<()> { fn try_file(kind: &str, path: &str) -> io::Result<()> {
let fruits_value = IOBinarySource::new(&mut File::open(path)?).packed_iovalues().demand_next(true)?; let fruits_value = IOBinarySource::new(&mut File::open(path)?).packed_iovalues().demand_next(true)?;
println!("{:?}", fruits_value); println!("{:#?}", fruits_value);
let fruits1: Vec<Fruit> = value::de::from_value(&fruits_value)?; let fruits1: Vec<Fruit> = value::de::from_value(&fruits_value)?;
println!("(via generic decoding) {}: {:?}", kind, fruits1); println!("(via generic decoding) {}: {:?}", kind, fruits1);

View File

@ -19,6 +19,17 @@ mod dom {
impl Domain for Dom {} impl Domain for Dom {}
impl std::str::FromStr for Dom {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
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<UnwrappedIOValue> { fn dom_as_preserves(v: &Dom) -> io::Result<UnwrappedIOValue> {
Ok(match v { Ok(match v {
Dom::One => Value::bytestring(vec![255, 255, 255, 255]), 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::<Value<ArcValue<Dom>>>().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::<Value<ArcValue<Dom>>>().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::<IOValue>().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)] #[cfg(test)]
mod serde_tests { mod serde_tests {
use crate::symbol::Symbol; use crate::symbol::Symbol;

View File

@ -4,6 +4,7 @@ use super::BinarySource;
use super::BytesBinarySource; use super::BytesBinarySource;
use super::Embeddable; use super::Embeddable;
use super::IOValue; use super::IOValue;
use super::NestedValue;
use super::Reader; use super::Reader;
use super::Writer; use super::Writer;
use super::packed; use super::packed;
@ -50,6 +51,22 @@ impl<'a, D: Embeddable, T: DomainDecode<D>> DomainDecode<D> for &'a mut T {
} }
} }
pub struct DebugDomainEncode;
impl<D: Embeddable> DomainEncode<D> for DebugDomainEncode {
fn encode_embedded<W: Writer>(&mut self, w: &mut W, d: &D) -> io::Result<()> {
d.debug_encode(w)
}
}
pub struct FromStrDomainParse;
impl<Err: Into<io::Error>, D: Embeddable + std::str::FromStr<Err = Err>> DomainParse<D> for FromStrDomainParse {
fn parse_embedded(&mut self, v: &IOValue) -> io::Result<D> {
Ok(D::from_str(v.value().to_string()?).map_err(|e| e.into())?)
}
}
pub struct IOValueDomainCodec; pub struct IOValueDomainCodec;
impl DomainDecode<IOValue> for IOValueDomainCodec { impl DomainDecode<IOValue> for IOValueDomainCodec {

View File

@ -17,6 +17,8 @@ pub use de::from_value;
pub use domain::DomainDecode; pub use domain::DomainDecode;
pub use domain::DomainEncode; pub use domain::DomainEncode;
pub use domain::DomainParse; pub use domain::DomainParse;
pub use domain::DebugDomainEncode;
pub use domain::FromStrDomainParse;
pub use domain::IOValueDomainCodec; pub use domain::IOValueDomainCodec;
pub use domain::NoEmbeddedDomainCodec; pub use domain::NoEmbeddedDomainCodec;
pub use domain::ViaCodec; pub use domain::ViaCodec;

View File

@ -7,6 +7,7 @@ use std::convert::TryFrom;
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::io;
use std::ops::Index; use std::ops::Index;
use std::ops::IndexMut; use std::ops::IndexMut;
use std::string::String; use std::string::String;
@ -16,14 +17,29 @@ use std::vec::Vec;
pub use std::collections::BTreeSet as Set; pub use std::collections::BTreeSet as Set;
pub use std::collections::BTreeMap as Map; 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::signed_integer::SignedInteger;
use super::text;
use crate::error::{Error, ExpectedKind, Received}; 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<W: Writer>(&self, w: &mut W) -> io::Result<()> {
w.write_string(&format!("{:?}", self))
}
}
pub trait Embeddable: Domain + Clone {} pub trait Embeddable: Domain + Clone {}
impl<T> Embeddable for T where T: Domain + Clone {} impl<T> Embeddable for T where T: Domain + Clone {}
impl<D: Domain> Domain for Arc<D> {} impl<D: Domain> Domain for Arc<D> {
fn debug_encode<W: Writer>(&self, w: &mut W) -> io::Result<()> {
self.as_ref().debug_encode(w)
}
}
pub trait NestedValue: Sized + Debug + Clone + Eq + Hash + Ord { pub trait NestedValue: Sized + Debug + Clone + Eq + Hash + Ord {
type Embedded: Embeddable; type Embedded: Embeddable;
@ -312,46 +328,18 @@ impl<N: NestedValue> From<Set<N>> for Value<N> { fn from(v: Set<N>) -> Self { Va
impl<N: NestedValue> From<Map<N, N>> for Value<N> { fn from(v: Map<N, N>) -> Self { Value::Dictionary(v) } } impl<N: NestedValue> From<Map<N, N>> for Value<N> { fn from(v: Map<N, N>) -> Self { Value::Dictionary(v) } }
impl<N: NestedValue> Debug for Value<N> { impl<N: NestedValue> Debug for Value<N> {
// 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { TextWriter::fmt_value(f, &mut DebugDomainEncode, self).map_err(|_| std::fmt::Error)
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), impl<Err: Into<io::Error>, D: Embeddable + std::str::FromStr<Err = Err>, N: NestedValue<Embedded = D>>
Value::SignedInteger(v) => write!(f, "{}", v), std::str::FromStr for Value<N>
Value::String(v) => write!(f, "{:?}", v), // TODO: proper escaping! {
Value::ByteString(v) => { type Err = io::Error;
f.write_str("#x\"")?;
for b in v { write!(f, "{:02x}", b)? } fn from_str(s: &str) -> Result<Self, Self::Err> {
f.write_str("\"") Ok(text::from_str::<N, _>(s, FromStrDomainParse)?.value_owned())
}
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),
}
} }
} }
@ -1432,7 +1420,11 @@ impl<D: Embeddable> Debug for ArcValue<D> {
pub struct IOValue(Arc<AnnotatedValue<IOValue>>); pub struct IOValue(Arc<AnnotatedValue<IOValue>>);
pub type UnwrappedIOValue = Value<IOValue>; pub type UnwrappedIOValue = Value<IOValue>;
impl Domain for IOValue {} impl Domain for IOValue {
fn debug_encode<W: Writer>(&self, w: &mut W) -> io::Result<()> {
w.write(&mut IOValueDomainCodec, self)
}
}
impl NestedValue for IOValue { impl NestedValue for IOValue {
type Embedded = Self; 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<Self, Self::Err> {
text::annotated_iovalue_from_str(s)
}
}
impl serde::Serialize for IOValue { impl serde::Serialize for IOValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
super::magic::output_value(serializer, self.clone()) super::magic::output_value(serializer, self.clone())

View File

@ -29,11 +29,24 @@ pub struct TextWriter<W: io::Write> {
impl std::default::Default for CommaStyle { impl std::default::Default for CommaStyle {
fn default() -> Self { fn default() -> Self {
CommaStyle::None CommaStyle::Separating
} }
} }
impl TextWriter<&mut Vec<u8>> { impl TextWriter<&mut Vec<u8>> {
pub fn fmt_value<N: NestedValue, Enc: DomainEncode<N::Embedded>>(
f: &mut std::fmt::Formatter<'_>,
enc: &mut Enc,
v: &crate::value::Value<N>,
) -> io::Result<()> {
let mut buf: Vec<u8> = 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<N: NestedValue, Enc: DomainEncode<N::Embedded>>( pub fn encode<N: NestedValue, Enc: DomainEncode<N::Embedded>>(
enc: &mut Enc, enc: &mut Enc,
v: &N, v: &N,