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<()> {
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)?;
println!("(via generic decoding) {}: {:?}", kind, fruits1);

View File

@ -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<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> {
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::<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)]
mod serde_tests {
use crate::symbol::Symbol;

View File

@ -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<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;
impl DomainDecode<IOValue> for IOValueDomainCodec {

View File

@ -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;

View File

@ -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<W: Writer>(&self, w: &mut W) -> io::Result<()> {
w.write_string(&format!("{:?}", self))
}
}
pub trait Embeddable: 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 {
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> 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 {
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<Err: Into<io::Error>, D: Embeddable + std::str::FromStr<Err = Err>, N: NestedValue<Embedded = D>>
std::str::FromStr for Value<N>
{
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(text::from_str::<N, _>(s, FromStrDomainParse)?.value_owned())
}
}
@ -1432,7 +1420,11 @@ impl<D: Embeddable> Debug for ArcValue<D> {
pub struct IOValue(Arc<AnnotatedValue<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 {
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 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
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 {
fn default() -> Self {
CommaStyle::None
CommaStyle::Separating
}
}
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>>(
enc: &mut Enc,
v: &N,