diff --git a/implementations/rust/oo/src/lib.rs b/implementations/rust/oo/src/lib.rs index 46f3d0e..531c89e 100644 --- a/implementations/rust/oo/src/lib.rs +++ b/implementations/rust/oo/src/lib.rs @@ -32,6 +32,8 @@ pub use repr::Record; pub use repr::Set; pub use repr::Symbol; pub use repr::Value; +pub use repr::copy_via; +pub use repr::iovalue; pub use repr::owned; pub use repr::value; pub use signed_integer::SignedInteger; @@ -92,3 +94,84 @@ mod demo { // } } } + +#[cfg(test)] +mod test_domain { + use std::io; + use crate::*; + + #[derive(Debug, Hash, Clone, Ord, PartialEq, Eq, PartialOrd)] + pub enum Dom { + One, + Two, + } + + impl Domain for Dom { + type Decode = DebugDomainCodec; + type Encode = DebugDomainCodec; + } + + 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")), + } + } + } + + struct DomCodec; + + impl DomainDecode for DomCodec { + fn decode_embedded<'de, R: Reader<'de> + ?Sized>( + &mut self, + r: &mut R, + _read_annotations: bool, + ) -> io::Result { + let v = r.next_iovalue(false)?; + if v.as_bytestring().is_some() { + Ok(Dom::One) + } else { + Ok(Dom::Two) + } + } + } + + impl DomainEncode for DomCodec { + fn encode_embedded( + &mut self, + w: &mut dyn Writer, + d: &Dom, + ) -> io::Result<()> { + match d { + Dom::One => Bytes::new(vec![255, 255, 255, 255]).write(w, self), + Dom::Two => Symbol::new(&format!("Dom::{:?}", d)).write(w, self), + } + } + } + + fn dom_as_preserves(v: &Dom) -> io::Result> { + Ok(match v { + Dom::One => owned(Bytes::new(vec![255, 255, 255, 255])), + Dom::Two => owned(Symbol::new(format!("Dom::{:?}", v))), + }) + } + + #[test] fn test_one() { + let v = owned(vec![owned(1), + owned(Embedded::new(Dom::One)), + owned(2)]); + assert_eq!(PackedWriter::encode_iovalue(&iovalue(copy_via(&v, &mut dom_as_preserves).unwrap())).unwrap(), + [0xb5, 0x91, 0xb2, 0x04, 255, 255, 255, 255, 0x92, 0x84]); + } + + #[test] fn test_two() { + let v = owned(vec![owned(1), + owned(Embedded::new(Dom::Two)), + owned(2)]); + assert_eq!(PackedWriter::encode_iovalue(&iovalue(copy_via(&v, &mut dom_as_preserves).unwrap())).unwrap(), + [0xb5, 0x91, 0xb3, 0x08, 68, 111, 109, 58, 58, 84, 119, 111, 0x92, 0x84]); + } +} diff --git a/implementations/rust/oo/src/repr.rs b/implementations/rust/oo/src/repr.rs index cd8dd14..0e8eda8 100644 --- a/implementations/rust/oo/src/repr.rs +++ b/implementations/rust/oo/src/repr.rs @@ -1,5 +1,6 @@ use bytemuck::TransparentWrapper; +use std::any::Any; use std::borrow::{Cow, Borrow}; use std::cmp::Ordering; use std::fmt::Debug; @@ -25,6 +26,7 @@ use crate::domain::{NoEmbeddedDomainCodec, DomainEncode, IOValueDomainCodec}; use super::float::{eq_f32, eq_f64, cmp_f32, cmp_f64}; pub type PlainValue<'a, D = IOValue> = Box + 'a>; +pub type ArcValue = Arc>; /// Atomic values from the specification. pub trait Value { @@ -65,6 +67,8 @@ pub trait Value { fn embedded(&self) -> Cow<'_, D> { panic!("Not an embedded value") } fn annotations(&self) -> Option<&[IOValue]> { None } + + fn specialized(&self) -> Option<&dyn Any> { None } } pub fn value>(v: &V) -> &dyn Value { @@ -79,6 +83,40 @@ pub fn iovalue + 'static>(v: V) -> IOValue { IOValue(Arc::new(v)) } +pub fn copy_via( + v: &dyn Value, + f: &mut F, +) -> Result, Err> +where + F: FnMut(&D) -> Result, Err> +{ + match v.value_class() { + ValueClass::Atomic(a) => Ok(match a { + AtomClass::Boolean => owned(v.as_boolean().unwrap()), + AtomClass::Float => owned(v.as_float().unwrap()), + AtomClass::Double => owned(v.as_double().unwrap()), + AtomClass::SignedInteger => owned(v.as_signed_integer().unwrap()), + AtomClass::String => owned(v.as_string().unwrap().into_owned()), + AtomClass::ByteString => owned(Bytes(v.as_bytestring().unwrap().into_owned())), + AtomClass::Symbol => owned(Symbol(v.as_symbol().unwrap().into_owned())), + }), + ValueClass::Compound(c) => Ok(match c { + CompoundClass::Sequence => + owned(v.iter().map(|w| copy_via(w, f)).collect::, _>>()?), + CompoundClass::Set => + owned(v.iter().map(|w| copy_via(w, f)).collect::, _>>()?), + CompoundClass::Record => + owned(Record::new( + copy_via(v.label(), f)?, + v.iter().map(|w| copy_via(w, f)).collect::, _>>()?)), + CompoundClass::Dictionary => + owned(v.entries().map(|(k, w)| Ok((copy_via(k, f)?, copy_via(w, f)?))) + .collect::, _>>()?), + }), + ValueClass::Embedded => f(&v.embedded()), + } +} + impl<'a, D: Domain + 'static> From<&'a dyn Value> for PlainValue<'static, D> { fn from(v: &'a dyn Value) -> Self { v.value_clone() @@ -490,7 +528,13 @@ impl Value for String { #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(transparent)] -pub struct Bytes>(T); +pub struct Bytes + Debug = Vec>(T); + +impl + Debug> Bytes { + pub fn new(t: T) -> Self { + Bytes(t) + } +} impl + Debug, D: Domain> Value for Bytes { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_bytes(self.0.as_ref()) } @@ -504,8 +548,8 @@ impl + Debug, D: Domain> Value for Bytes { pub struct Symbol + Debug = String>(T); impl + Debug> Symbol { - pub fn new>(t: R) -> Self { - Symbol(t.into()) + pub fn new(t: T) -> Self { + Symbol(t) } } @@ -702,8 +746,8 @@ impl<'k, D: Domain, V: Value, K: for<'a> Borrow> + Ord + 'k> Value pub struct Embedded(D); impl Embedded { - pub fn new>(d: E) -> Self { - Embedded(d.into()) + pub fn new(d: D) -> Self { + Embedded(d) } pub fn embedded_value(&self) -> &D { @@ -820,7 +864,7 @@ impl> Ord for Annotations { } #[derive(Clone, Eq, Hash, PartialOrd, Ord)] -pub struct IOValue(Arc>); +pub struct IOValue(ArcValue); impl Debug for IOValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {