wolfram_wxf 0.7.0

Convert a value to the Wolfram Language WXF format.
Documentation
use crate::{packed_array::WolframArray, WolframFunction, WolframRule, WolframSymbol, WolframValue};
use flate2::{write::ZlibEncoder, Compression};
use integer_encoding::VarIntWriter;
use ndarray::{Array, IxDyn};
use std::io::Write;

impl WolframValue {
    /// Encode a value into a byte vector.
    pub fn to_string(&self) -> String {
        format!("{}", self)
    }
    /// Encode a value into a byte vector.
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut out = Vec::new();
        out.extend_from_slice(b"8:");
        self.write_bytes(&mut out).unwrap();
        return out;
    }
    /// Encode a value into a compressed byte vector.
    pub fn to_compressed(&self) -> Vec<u8> {
        let mut input = Vec::new();
        let mut e = ZlibEncoder::new(vec![], Compression::new(9));
        self.write_bytes(&mut input).unwrap();
        let mut out = Vec::with_capacity(input.len());
        match e.write_all(&input) {
            Ok(_) => out.extend_from_slice(b"8C:"),
            Err(..) => {}
        };
        match e.finish() {
            Ok(o) => out.extend_from_slice(&o),
            Err(..) => {
                panic!()
            }
        };
        return out;
    }
    /// Write value to a byte vector.
    pub fn write_bytes<W: Write>(&self, out: &mut W) -> std::io::Result<()> {
        match self {
            WolframValue::Skip => (),
            WolframValue::Function(v) => v.write_bytes_inner(out)?,
            WolframValue::Boolean(v) => WolframSymbol::boolean(*v).write_bytes_inner(out)?,
            WolframValue::String(s) => {
                out.write(&[b'S'])?;
                out.write_varint(s.len())?;
                out.write(s.as_bytes())?;
            }
            WolframValue::Bytes(v) => {
                out.write(&[b'B'])?;
                out.write_varint(v.len())?;
                out.write(v)?;
            }
            WolframValue::Symbol(symbol) => symbol.write_bytes_inner(out)?,
            WolframValue::Integer8(n) => {
                out.write(&[b'C'])?;
                out.write(&n.to_le_bytes())?;
            }
            WolframValue::Integer16(n) => {
                out.write(&[b'j'])?;
                out.write(&n.to_le_bytes())?;
            }
            WolframValue::Integer32(n) => {
                out.write(&[b'i'])?;
                out.write(&n.to_le_bytes())?;
            }
            WolframValue::Integer64(n) => {
                out.write(&[b'L'])?;
                out.write(&n.to_le_bytes())?;
            }
            WolframValue::BigInteger(i) => {
                out.write(&[b'I'])?;
                let n = i.to_str_radix(10);
                out.write_varint(n.len())?;
                out.write(n.as_bytes())?;
            }
            WolframValue::Decimal64(s) => {
                out.write(&[b'r'])?;
                out.write(&s.to_le_bytes())?;
            }
            WolframValue::BigDecimal(_) => unimplemented!(),
            WolframValue::PackedArray(v) => {
                out.write(&[193])?;
                v.write_packed_array(out)?;
            }
            WolframValue::NumericArray(v) => {
                out.write(&[194])?;
                v.write_numberic_array(out)?;
            }
            WolframValue::Association(dict) => {
                out.write(&[b'A'])?;
                out.write_varint(dict.len())?;
                for (k, (r, v)) in dict {
                    r.write_bytes(out)?;
                    k.write_bytes(out)?;
                    v.write_bytes(out)?;
                }
            }
        }
        Ok(())
    }
}

impl WolframArray {
    fn write_numberic_array<W: Write>(&self, out: &mut W) -> std::io::Result<()> {
        match self {
            Self::Integer8(_) => {
                unimplemented!()
            }
            Self::Integer16(_) => {
                unimplemented!()
            }
            Self::Integer32(_) => {
                unimplemented!()
            }
            Self::Integer64(_) => {
                unimplemented!()
            }
            Self::Decimal32(_) => {
                unimplemented!()
            }
            Self::Decimal64(_) => {
                unimplemented!()
            }
            Self::Complex32(data) => {
                out.write(&[50])?;
                self.push_array_dim(data.clone(), out)?;
                for v in data.iter() {
                    out.write(&v.re.to_le_bytes())?;
                    out.write(&v.im.to_le_bytes())?;
                }
            }
            WolframArray::Complex64(data) => {
                out.write(&[51])?;
                self.push_array_dim(data.clone(), out)?;
                for v in data.iter() {
                    out.write(&v.re.to_le_bytes())?;
                    out.write(&v.im.to_le_bytes())?;
                }
            }
        }
        Ok(())
    }
    fn write_packed_array<W: Write>(&self, out: &mut W) -> std::io::Result<()> {
        match self {
            Self::Integer8(_) => {
                unimplemented!()
            }
            Self::Integer16(_) => {
                unimplemented!()
            }
            Self::Integer32(_) => {
                unimplemented!()
            }
            Self::Integer64(_) => {
                unimplemented!()
            }
            Self::Decimal32(_) => {
                unimplemented!()
            }
            Self::Decimal64(_) => {
                unimplemented!()
            }
            Self::Complex32(data) => {
                out.write(&[51])?;
                self.push_array_dim(data.clone(), out)?;
                for v in data.iter() {
                    out.write(&v.re.to_le_bytes())?;
                    out.write(&v.im.to_le_bytes())?;
                }
            }
            WolframArray::Complex64(data) => {
                out.write(&[52])?;
                self.push_array_dim(data.clone(), out)?;
                for v in data.iter() {
                    out.write(&v.re.to_le_bytes())?;
                    out.write(&v.im.to_le_bytes())?;
                }
            }
        }
        Ok(())
    }
    fn push_array_dim<W: Write, T>(&self, array: Array<T, IxDyn>, out: &mut W) -> std::io::Result<()> {
        // push rank
        out.write_varint(array.ndim())?;
        for dim in array.shape() {
            // push dimension
            out.write_varint(*dim)?;
        }
        Ok(())
    }
}

impl WolframFunction {
    fn write_bytes_inner<W: Write>(&self, out: &mut W) -> std::io::Result<()> {
        out.write(&[b'f'])?;
        out.write_varint(self.get_rest().len())?;
        self.get_head().write_bytes(out)?;
        for v in self.get_rest() {
            v.write_bytes(out)?
        }
        Ok(())
    }
}

impl WolframSymbol {
    fn write_bytes_inner<W: Write>(&self, out: &mut W) -> std::io::Result<()> {
        let s = format!("{:?}", self);
        out.write(&[b's'])?;
        out.write_varint(s.len())?;
        out.write(s.as_bytes())?;
        Ok(())
    }
}

impl WolframRule {
    fn write_bytes<W: Write>(&self, out: &mut W) -> std::io::Result<()> {
        match self {
            Self::Rule => out.write(&[b'-'])?,
            Self::RuleDelayed => out.write(&[b':'])?,
        };
        Ok(())
    }
}