b3-rs 0.1.0

A Rust implementation of B3 (Better Binary Buffers)
//! Data type definitions.

use crate::alloc_prelude::*;
use nano_leb128::ULEB128;

/// The B3 standard-defined data types.
///
/// The standard-defined data type `Complex` (type number 16) is an extended
/// type, and as such, is found in the [`KnownExtendedType`] enum.
#[repr(u64)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KnownType {
    /// Type 0 - Reserved, probably permanently unused.
    Reserved0 = 0,

    /// Type 1 - Dictionary-like object.
    ///
    /// Identical to `CompositeList` on the wire, hints to parser to yield a
    /// dictionary-like object where possible.
    CompositeDict = 1,

    /// Type 2 - List-like object.
    ///
    /// Identical to `CompositeDict` on the wire, hints to parser to yield a
    /// list-like object where possible.
    CompositeList = 2,

    /// Type 3 - Array of bytes.
    Bytes = 3,

    /// Type 4 - A UTF-8 encoded string.
    UTF8 = 4,

    /// Type 5 - A boolean.
    Boolean = 5,

    /// Type 6 - A signed 64-bit integer.
    Int64 = 6,

    /// Type 7 - An unsigned variable length integer.
    UVarInt = 7,

    /// Type 8 - A signed variable length integer.
    SVarInt = 8,

    /// Type 9 - A signed 64-bit float.
    Float64 = 9,

    /// Type 10 - Arbitrary-precision decimal.
    Decimal = 10,

    /// Type 11 - Reserved, currently unused.
    Reserved11 = 11,

    /// Type 12 - Signed 64-bit UNIX nanoseconds.
    Stamp64 = 12,

    /// Type 13 - Local datetime.
    ///
    /// YMDHMS plus optional subseconds, offset to UTC, and timezone name.
    Sched = 13,

    /// Type 14 - Reserved, currently unused.
    Reserved14 = 14,

    /// Type 15 - Reserved, used as a marker to signify that there is an
    /// extended data type in a `UVarInt` following the item's control byte.
    ExtendedType = 15,
}

impl From<u64> for KnownType {
    fn from(i: u64) -> Self {
        match i {
            0 => Self::Reserved0,
            1 => Self::CompositeDict,
            2 => Self::CompositeList,
            3 => Self::Bytes,
            4 => Self::UTF8,
            5 => Self::Boolean,
            6 => Self::Int64,
            7 => Self::UVarInt,
            8 => Self::SVarInt,
            9 => Self::Float64,
            10 => Self::Decimal,
            11 => Self::Reserved11,
            12 => Self::Stamp64,
            13 => Self::Sched,
            14 => Self::Reserved14,
            _ => Self::ExtendedType,
        }
    }
}

/// The B3 standard-defined extended data types.
#[repr(u64)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KnownExtendedType {
    /// An unknown type.
    ///
    /// Provided only for convenience for `From` implementations.
    Unknown = 0,

    /// Type 16 - Complex number.
    ///
    /// Encoded as two `Float64`s.
    Complex = 16,
}

impl From<u64> for KnownExtendedType {
    fn from(i: u64) -> Self {
        match i {
            16 => Self::Complex,
            _ => Self::Unknown,
        }
    }
}

/// Data type container.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DataType {
    /// The null data type.
    Null,

    /// A standard-defined data type.
    ///
    /// The type is encoded in the "data type" bits of an item's control byte.
    StandardType(KnownType),

    /// A standard-defined extended data type.
    ///
    /// The type is encoded as a `UVarInt` (unsigned LEB128) added after the
    /// item's control byte, and the "data type" bits of the control byte are
    /// all set to `1`.
    StandardExtendedType(KnownExtendedType),

    /// A user-defined extended data type.
    ///
    /// The type is encoded as a `UVarInt` (unsigned LEB128) added after the
    /// item's control byte, and the "data type" bits of the control byte are
    /// all set to `1`.
    ///
    /// Data type numbers 96 through 8191 (inclusive) are available for use
    /// as user-defined types.
    UserExtendedType(u64),
}

impl DataType {
    pub fn encode(&self) -> Result<(u8, Vec<u8>), crate::Error> {
        let mut out: Vec<u8> = Vec::new();
        let mut len_bytes = [0u8; 10];

        match self {
            Self::Null => {
                return Ok((0, out));
            }

            Self::StandardType(t) => {
                return Ok(((*t as u8) & 0x0F, out));
            }

            Self::StandardExtendedType(t) => {
                let count = ULEB128::from(*t as u64).write_into(&mut len_bytes)?;
                out.extend(&len_bytes[0..count]);
            }

            Self::UserExtendedType(t) => {
                let count = ULEB128::from(*t).write_into(&mut len_bytes)?;
                out.extend(&len_bytes[0..count]);
            }
        }

        Ok((0x0Fu8, out))
    }
}

impl From<KnownType> for DataType {
    fn from(t: KnownType) -> Self {
        Self::StandardType(t)
    }
}

impl From<KnownExtendedType> for DataType {
    fn from(t: KnownExtendedType) -> Self {
        Self::StandardExtendedType(t)
    }
}

impl From<u64> for DataType {
    fn from(i: u64) -> Self {
        if i < 15 {
            Self::StandardType(KnownType::from(i))
        } else if i < 96 {
            Self::StandardExtendedType(KnownExtendedType::from(i))
        } else {
            Self::UserExtendedType(i)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn encode_type_standard() {
        let ty = DataType::StandardType(KnownType::CompositeDict);
        let (base, ext) = ty.encode().unwrap();
        assert_eq!(base, 0x01);
        assert_eq!(ext.len(), 0);

        let ty = DataType::StandardType(KnownType::Float64);
        let (base, ext) = ty.encode().unwrap();
        assert_eq!(base, 0x09);
        assert_eq!(ext.len(), 0);
    }

    #[test]
    fn encode_type_standard_extended() {
        let ty = DataType::StandardExtendedType(KnownExtendedType::Complex);
        let (base, ext) = ty.encode().unwrap();
        assert_eq!(base, 0x0F);
        assert_eq!(ext, vec![0x10]);
    }

    #[test]
    fn encode_type_user_extended() {
        let ty = DataType::UserExtendedType(624485);
        let (base, ext) = ty.encode().unwrap();
        assert_eq!(base, 0x0F);
        assert_eq!(ext, vec![0xE5, 0x8E, 0x26]);
    }
}