preserves 5.0.0-rc.9

Implementation of the Preserves serialization format.
Documentation
pub mod atom;
pub mod boundary;
pub mod domain;
pub mod error;
pub mod float;
pub mod hex;
pub mod iovalue;
pub mod merge;
pub mod packed;
pub mod reader;
pub mod repr;
pub mod shell;
pub mod signed_integer;
pub mod source;
pub mod text;
pub mod types;
pub mod writer;

#[cfg(feature = "serde")]
pub mod serde;

pub use atom::Atom;
pub use domain::*;
pub use error::Error;
pub use error::SyntaxError;
pub use error::ExpectedKind;
pub use error::UnexpectedKind;
pub use iovalue::IOValue;
pub use iovalue::IOValueDomainDecode;
pub use iovalue::IOValueDomainEncode;
pub use merge::merge2;
pub use merge::merge;
pub use packed::PackedReader;
pub use packed::PackedWriter;
pub use packed::View;
pub use packed::view::ViewValueReader;
pub use packed::view::ViewStream;
pub use packed::view;
pub use packed::iovalue_view;
pub use reader::IOValueReader;
pub use reader::IOValueStream;
pub use reader::Reader;
pub use reader::ReaderResult;
pub use reader::ValueReader;
pub use reader::ValueStream;
pub use repr::Value;
pub use repr::ValueImpl;
pub use repr::ValueObject;
pub use repr::value_cmp;
pub use repr::value_deepcopy;
pub use repr::value_deepcopy_via;
pub use repr::value_eq;
pub use repr::value_hash;
pub use repr::value_map_embedded;
pub use shell::Annotations;
pub use shell::Bytes;
pub use shell::Double;
pub use shell::Embedded;
pub use shell::Map;
pub use shell::Record;
pub use shell::Set;
pub use shell::Symbol;
pub use shell::TreeValueReader;
pub use signed_integer::SignedInteger;
pub use source::BinarySource;
pub use source::BytesBinarySource;
pub use source::IOBinarySource;
pub use text::TextReader;
pub use text::TextWriter;
pub use types::AtomClass;
pub use types::CompoundClass;
pub use types::ValueClass;
pub use writer::Writer;
pub use writer::write_value;

/// Retrieve the version of the Preserves crate.
pub const fn preserves_package_version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

/// Retrieve the Preserves format specification version implemented by this crate.
/// See <https://preserves.dev/changes.html>.
pub const fn preserves_spec_version() -> &'static str {
    "0.996.3"
}

pub fn read_text<D: Domain, Dec: DomainDecode<D>>(
    s: &str,
    read_annotations: bool,
    dec: &mut Dec,
) -> ReaderResult<Value<D>> {
    TreeValueReader::read_text(s, read_annotations, dec)
}

pub fn read_packed<D: Domain, Dec: DomainDecode<D>>(
    bs: &[u8],
    read_annotations: bool,
    dec: &mut Dec,
) -> ReaderResult<Value<D>> {
    if cfg!(feature = "view-by-default") {
        ViewValueReader::read_packed(bs, read_annotations, dec)
    } else {
        TreeValueReader::read_packed(bs, read_annotations, dec)
    }
}

pub fn read_iovalue_text(s: &str, read_annotations: bool) -> ReaderResult<IOValue> {
    TreeValueReader::read_iovalue_text(s, read_annotations)
}

pub fn read_iovalue_packed(bs: &[u8], read_annotations: bool) -> ReaderResult<IOValue> {
    if cfg!(feature = "view-by-default") {
        ViewValueReader::read_iovalue_packed(bs, read_annotations)
    } else {
        TreeValueReader::read_iovalue_packed(bs, read_annotations)
    }
}

pub fn read_all_packed<D: Domain, Dec: DomainDecode<D>>(
    bs: &[u8],
    read_annotations: bool,
    dec: &mut Dec,
) -> ReaderResult<Vec<Value<D>>> {
    if cfg!(feature = "view-by-default") {
        let bs: std::sync::Arc<[u8]> = bs.into();
        ViewStream::new(bs, dec, read_annotations)
            .map(|v| v.map(|v| Value::new(v)))
            .collect()
    } else {
        BytesBinarySource::new(bs).into_packed().read_all(read_annotations, dec)
    }
}

pub fn read_all_text<D: Domain, Dec: DomainDecode<D>>(
    s: &str,
    read_annotations: bool,
    dec: &mut Dec,
) -> ReaderResult<Vec<Value<D>>> {
    BytesBinarySource::new(s.as_bytes()).into_text().read_all(read_annotations, dec)
}

pub fn read_all_iovalues_packed(
    bs: &[u8],
    read_annotations: bool,
) -> ReaderResult<Vec<IOValue>> {
    if cfg!(feature = "view-by-default") {
        let bs: std::sync::Arc<[u8]> = bs.into();
        ViewStream::new_iovalues(bs, read_annotations)
            .map(|v| v.map(|v| IOValue::new(v)))
            .collect()
    } else {
        BytesBinarySource::new(bs).into_packed().read_all_iovalues(read_annotations)
    }
}

pub fn read_all_iovalues_text(
    s: &str,
    read_annotations: bool,
) -> ReaderResult<Vec<IOValue>> {
    BytesBinarySource::new(s.as_bytes()).into_text().read_all_iovalues(read_annotations)
}

pub fn write_packed_into<D: Domain, Enc: DomainEncode<D>, W: std::io::Write>(
    v: &Value<D>,
    write_annotations: bool,
    enc: &mut Enc,
    sink: &mut W,
) -> std::io::Result<()> {
    v.write(&mut PackedWriter::new(sink).set_write_annotations(write_annotations), enc)
}

pub fn write_iovalue_packed_into<W: std::io::Write>(
    v: &IOValue,
    write_annotations: bool,
    sink: &mut W,
) -> std::io::Result<()> {
    write_packed_into(v.into(), write_annotations, &mut IOValueDomainEncode, sink)
}

pub fn write_packed<D: Domain, Enc: DomainEncode<D>>(
    v: &Value<D>,
    write_annotations: bool,
    enc: &mut Enc,
) -> std::io::Result<Vec<u8>> {
    let mut sink = Vec::new();
    write_packed_into(v, write_annotations, enc, &mut sink)?;
    Ok(sink)
}

pub fn write_iovalue_packed(
    v: &IOValue,
    write_annotations: bool,
) -> std::io::Result<Vec<u8>> {
    write_packed(v.into(), write_annotations, &mut IOValueDomainEncode)
}

pub fn write_text_into<D: Domain, Enc: DomainEncode<D>, W: std::io::Write>(
    v: &Value<D>,
    write_annotations: bool,
    enc: &mut Enc,
    sink: &mut W,
) -> std::io::Result<()> {
    v.write(&mut TextWriter::new(sink).set_write_annotations(write_annotations), enc)
}

pub fn write_iovalue_text_into<W: std::io::Write>(
    v: &IOValue,
    write_annotations: bool,
    sink: &mut W,
) -> std::io::Result<()> {
    write_text_into(v.into(), write_annotations, &mut IOValueDomainEncode, sink)
}

pub fn write_text<D: Domain, Enc: DomainEncode<D>>(
    v: &Value<D>,
    write_annotations: bool,
    enc: &mut Enc,
) -> std::io::Result<String> {
    let mut sink = Vec::new();
    write_text_into(v, write_annotations, enc, &mut sink)?;
    Ok(String::from_utf8(sink).expect("valid UTF-8 from TextWriter"))
}

pub fn write_iovalue_text(
    v: &IOValue,
    write_annotations: bool,
) -> std::io::Result<String> {
    write_text(v.into(), write_annotations, &mut IOValueDomainEncode)
}

#[cfg(test)]
mod demo {
    use crate::*;

    // fn getit<'a>(d: &'a ValueObject<NoValue>, k: &str) -> Option<&'a ValueObject<NoValue>> {
    //     d.get(&k)
    // }

    #[test] fn a() {
        let l: IOValue = "label".parse().unwrap();
        let r = IOValue::record(l.clone(), vec![IOValue::new(1), IOValue::new(2), IOValue::new(3)]);
        let r2 = IOValue::record(l, vec![IOValue::new(1), IOValue::new(2), IOValue::new(4)]);

        let mut v: Map<IOValue, IOValue> = Map::new();
        v.insert("\"abc\"".parse().unwrap(), "def".parse().unwrap());
        v.insert("abc".parse().unwrap(), "DEF".parse().unwrap());
        v.insert(IOValue::new(123), "xyz".parse().unwrap());
        v.insert(IOValue::new(vec![1, 2, 3]), "{a: 1, b: 2}".parse().unwrap());
        v.insert(r2, "bbb".parse().unwrap());
        v.insert(r, "<foo bar zot>".parse().unwrap());
        let w: &ValueObject<IOValue> = &v;
        println!("GETw abc {:?}", w.get(&IOValue::new("abc").into()));
        println!("GETw 123 {:?}", w.get(&IOValue::new(123).into()));
        println!("GETw qqq {:?}", w.get(&IOValue::new("qqq").into()));
        println!("GETv abc {:?}", v.get(&IOValue::new("abc")));
        println!("GETv 123 {:?}", v.get(&IOValue::new(123)));
        println!("GETv qqq {:?}", v.get(&IOValue::new("qqq")));
        for (kk, vv) in w.entries() {
            println!("{:#?} ==> {:#?}", kk, vv);
        }

        // {
        //     use std::io::BufRead;
        //     for line in std::io::stdin().lock().lines() {
        //         let line = line.unwrap();
        //         let key = line.parse::<PlainValue<NoValue>>().unwrap();
        //         let val = w.get(&key);
        //         println!("{:?} == {:?} ==> {:?} == {:?}", line, &key, val, getit(&v, &line));
        //     }
        // }
    }

    #[test] fn value_size() {
        println!("Value size {}", std::mem::size_of::<Value<IOValue>>());
        println!("&ValueObject size {}", std::mem::size_of::<&ValueObject<IOValue>>());
        println!("Box ValueObject size {}", std::mem::size_of::<Box<ValueObject<IOValue>>>());
        println!("Arc ValueObject size {}", std::mem::size_of::<std::sync::Arc<ValueObject<IOValue>>>());
        println!("View &'static [u8] size {}", std::mem::size_of::<View<NoEmbedded, &'static [u8]>>());
        println!("View Arc<[u8]> size {}", std::mem::size_of::<View<NoEmbedded, std::sync::Arc<[u8]>>>());
    }
}

#[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 {}

    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")),
            }
        }
    }

    struct DomCodec;

    impl DomainDecode<Dom> for DomCodec {
        fn decode_value(&mut self, v: IOValue) -> reader::ReaderResult<Dom> {
            if v.as_bytestring().is_some() {
                Ok(Dom::One)
            } else {
                Ok(Dom::Two)
            }
        }
    }

    impl DomainEncode<Dom> for DomCodec {
        fn encode_value(&mut self, d: &Dom) -> io::Result<IOValue> {
            Ok(match d {
                Dom::One => IOValue::bytes(vec![255, 255, 255, 255]),
                Dom::Two => IOValue::symbol(format!("Dom::{:?}", d)),
            })
        }
    }

    fn dom_as_preserves(v: &Dom) -> io::Result<IOValue> {
        Ok(match v {
            Dom::One => IOValue::bytes(vec![255, 255, 255, 255]),
            Dom::Two => IOValue::symbol(format!("Dom::{:?}", v)),
        })
    }

    #[test] fn test_one() {
        let v = Value::new(
            vec![Value::new(SignedInteger::from(1)),
                 Value::embedded(Dom::One),
                 Value::new(SignedInteger::from(2))]);
        assert_eq!(PackedWriter::encode_iovalue(&value_deepcopy_via(&v, &mut dom_as_preserves).unwrap().into()).unwrap(),
                   [0xb5,
                    0xb0, 0x01, 0x01,
                    0xb2, 0x04, 255, 255, 255, 255,
                    0xb0, 0x01, 0x02,
                    0x84]);
        assert_eq!(PackedWriter::encode(&mut DomCodec, &v).unwrap(),
                   [0xb5,
                    0xb0, 0x01, 0x01,
                    0x86, 0xb2, 0x04, 255, 255, 255, 255,
                    0xb0, 0x01, 0x02,
                    0x84]);
    }

    #[test] fn test_two() {
        let v = Value::new(
            vec![Value::new(SignedInteger::from(1)),
                 Value::embedded(Dom::Two),
                 Value::new(SignedInteger::from(2))]);
        assert_eq!(PackedWriter::encode_iovalue(&value_deepcopy_via(&v, &mut dom_as_preserves).unwrap().into()).unwrap(),
                   [0xb5,
                    0xb0, 0x01, 0x01,
                    0xb3, 0x08, 68, 111, 109, 58, 58, 84, 119, 111,
                    0xb0, 0x01, 0x02,
                    0x84]);
        assert_eq!(PackedWriter::encode(&mut DomCodec, &v).unwrap(),
                   [0xb5,
                    0xb0, 0x01, 0x01,
                    0x86, 0xb3, 0x08, 68, 111, 109, 58, 58, 84, 119, 111,
                    0xb0, 0x01, 0x02,
                    0x84]);
    }
}