Luigit
repositories / smith

smith

There are many coding harnesses - but this one is fast

owned by admin

smith-core/src/cbor.rs

Raw
//! Smith-owned CBOR operations used by JSON content codecs (`SMH-SPEC-SPEC0001`).
//! Engine types stay private to this facade.

use ciborium_ll::{Decoder as EngineDecoder, Encoder as EngineEncoder, Header as EngineHeader};

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Header {
    Positive(u64),
    Negative(u64),
    Float(f64),
    Simple(u8),
    Text(Option<usize>),
    Array(Option<usize>),
    Map(Option<usize>),
}

impl TryFrom<EngineHeader> for Header {
    type Error = DecodeError;

    fn try_from(header: EngineHeader) -> Result<Self, Self::Error> {
        Ok(match header {
            EngineHeader::Positive(value) => Self::Positive(value),
            EngineHeader::Negative(value) => Self::Negative(value),
            EngineHeader::Float(value) => Self::Float(value),
            EngineHeader::Simple(value) => Self::Simple(value),
            EngineHeader::Text(length) => Self::Text(length),
            EngineHeader::Array(length) => Self::Array(length),
            EngineHeader::Map(length) => Self::Map(length),
            EngineHeader::Bytes(_) | EngineHeader::Tag(_) | EngineHeader::Break => {
                return Err(DecodeError);
            }
        })
    }
}

impl From<Header> for EngineHeader {
    fn from(header: Header) -> Self {
        match header {
            Header::Positive(value) => Self::Positive(value),
            Header::Negative(value) => Self::Negative(value),
            Header::Float(value) => Self::Float(value),
            Header::Simple(value) => Self::Simple(value),
            Header::Text(length) => Self::Text(length),
            Header::Array(length) => Self::Array(length),
            Header::Map(length) => Self::Map(length),
        }
    }
}

pub trait Write {
    type Error;

    fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error>;
    fn flush(&mut self) -> Result<(), Self::Error>;
}

struct EngineOutput<W>(W);

impl<W: Write> ciborium_io::Write for EngineOutput<W> {
    type Error = W::Error;

    fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
        self.0.write_all(bytes)
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        self.0.flush()
    }
}

pub struct Encoder<W: Write>(EngineEncoder<EngineOutput<W>>);

impl<W: Write> From<W> for Encoder<W> {
    fn from(output: W) -> Self {
        Self(EngineEncoder::from(EngineOutput(output)))
    }
}

impl<W: Write> Encoder<W> {
    #[expect(
        clippy::large_types_passed_by_value,
        reason = "the encoder consumes this dependency-shaped fixed-width CBOR tag"
    )]
    pub fn push(&mut self, header: Header) -> Result<(), W::Error> {
        self.0.push(header.into())
    }

    pub fn text(&mut self, value: &str, segment: impl Into<Option<usize>>) -> Result<(), W::Error> {
        self.0.text(value, segment)
    }
}

#[derive(Clone, Copy, Debug)]
pub struct DecodeError;

pub struct Decoder<'a>(EngineDecoder<&'a [u8]>);

impl<'a> From<&'a [u8]> for Decoder<'a> {
    fn from(bytes: &'a [u8]) -> Self {
        Self(EngineDecoder::from(bytes))
    }
}

impl Decoder<'_> {
    pub fn pull(&mut self) -> Result<Header, DecodeError> {
        self.0
            .pull()
            .map_err(|_| DecodeError)
            .and_then(Header::try_from)
    }

    pub fn offset(&mut self) -> usize {
        self.0.offset()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn decoder_size_stays_compact() {
        #[cfg(target_arch = "x86_64")]
        assert_eq!(std::mem::size_of::<super::Decoder<'static>>(), 40);
    }

    #[test]
    fn engine_types_do_not_escape_public_facade() {
        let source = include_str!("cbor.rs");
        assert!(
            !source
                .lines()
                .any(|line| line.trim_start().starts_with("pub use ciborium"))
        );
    }
}