//! Traits for working with Preserves [embedded //! values](https://preserves.dev/preserves.html#embeddeds). use std::io; use super::packed; use super::BinarySource; use super::BytesBinarySource; use super::Embeddable; use super::IOValue; use super::NestedValue; use super::Reader; use super::Writer; /// Implementations parse [IOValue]s to their own particular [Embeddable] values of type `D`. pub trait DomainParse { fn parse_embedded(&mut self, v: &IOValue) -> io::Result; } /// Implementations read and parse from `src` to produce [Embeddable] values of type `D`. pub trait DomainDecode { fn decode_embedded<'de, 'src, S: BinarySource<'de>>( &mut self, src: &'src mut S, read_annotations: bool, ) -> io::Result; } /// Implementations unparse and write `D`s to `w`, a [writer][crate::value::writer::Writer]. pub trait DomainEncode { fn encode_embedded(&mut self, w: &mut W, d: &D) -> io::Result<()>; } impl<'a, D: Embeddable, T: DomainParse> DomainParse for &'a mut T { fn parse_embedded(&mut self, v: &IOValue) -> io::Result { (**self).parse_embedded(v) } } impl<'a, D: Embeddable, T: DomainDecode> DomainDecode for &'a mut T { fn decode_embedded<'de, 'src, S: BinarySource<'de>>( &mut self, src: &'src mut S, read_annotations: bool, ) -> io::Result { (**self).decode_embedded(src, read_annotations) } } /// Convenience codec: use this as embedded codec for encoding (only) when embedded values /// should be serialized as Preserves `String`s holding their Rust [std::fmt::Debug] /// representation. pub struct DebugDomainEncode; impl DomainEncode for DebugDomainEncode { fn encode_embedded(&mut self, w: &mut W, d: &D) -> io::Result<()> { d.debug_encode(w) } } /// Convenience codec: use this as embedded codec for decoding (only) when embedded values are /// expected to conform to the syntax implicit in their [std::str::FromStr] implementation. 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())?) } } /// Use this as embedded codec when embedded data are already [IOValue]s that can be directly /// serialized and deserialized without further transformation. pub struct IOValueDomainCodec; impl DomainDecode for IOValueDomainCodec { fn decode_embedded<'de, 'src, S: BinarySource<'de>>( &mut self, src: &'src mut S, read_annotations: bool, ) -> io::Result { packed::PackedReader::new(src, IOValueDomainCodec).demand_next(read_annotations) } } impl DomainEncode for IOValueDomainCodec { fn encode_embedded(&mut self, w: &mut W, d: &IOValue) -> io::Result<()> { w.write(self, d) } } /// Use this as embedded codec to forbid use of embedded values; an [io::Error] is signalled. pub struct NoEmbeddedDomainCodec; impl DomainDecode for NoEmbeddedDomainCodec { fn decode_embedded<'de, 'src, S: BinarySource<'de>>( &mut self, _src: &'src mut S, _read_annotations: bool, ) -> io::Result { Err(io::Error::new( io::ErrorKind::Unsupported, "Embedded values not supported here", )) } } impl DomainEncode for NoEmbeddedDomainCodec { fn encode_embedded(&mut self, _w: &mut W, _d: &D) -> io::Result<()> { Err(io::Error::new( io::ErrorKind::Unsupported, "Embedded values not supported here", )) } } /// If some `C` implements [DomainDecode] but not [DomainParse], or vice versa, use `ViaCodec` /// to promote the one to the other. Construct instances with [ViaCodec::new]. pub struct ViaCodec(C); impl ViaCodec { /// Constructs a `ViaCodec` wrapper around an underlying codec of type `C`. pub fn new(c: C) -> Self { ViaCodec(c) } } impl> DomainParse for ViaCodec { fn parse_embedded(&mut self, v: &IOValue) -> io::Result { let bs = packed::PackedWriter::encode_iovalue(v)?; self.0 .decode_embedded(&mut BytesBinarySource::new(&bs), true) } } impl> DomainDecode for ViaCodec { fn decode_embedded<'de, 'src, S: BinarySource<'de>>( &mut self, src: &'src mut S, read_annotations: bool, ) -> io::Result { let v = src .packed(IOValueDomainCodec) .demand_next(read_annotations)?; self.0.parse_embedded(&v) } }