sakka 0.0.11

A low-level framework for implementing binary file format parsers and writers.
Documentation
use super::{Codec, Decode, Encode};
use crate::{Endian, Error, Reader, Writer};

#[derive(Debug)]
enum InvertedBoolCodecError {
    Core(Error),
    InvalidDiscriminant(u8),
}

impl From<Error> for InvertedBoolCodecError {
    fn from(value: Error) -> Self {
        Self::Core(value)
    }
}

struct InvertedBoolCodec;

impl Codec<bool> for InvertedBoolCodec {
    type Error = InvertedBoolCodecError;

    fn encode(value: &bool, writer: &mut Writer<()>) -> Result<(), Self::Error> {
        let encoded = if *value { 0u8 } else { 1u8 };
        writer.write(&encoded)?;
        Ok(())
    }

    fn decode(reader: &mut Reader<'_, ()>) -> Result<bool, Self::Error> {
        match reader.read::<u8>()? {
            0 => Ok(true),
            1 => Ok(false),
            raw => Err(InvertedBoolCodecError::InvalidDiscriminant(raw)),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
struct TestRecord {
    id: u16,
    tag: u8,
    active: bool,
}

impl Encode for TestRecord {
    type Error = Error;

    fn encode(&self, w: &mut Writer<()>) -> Result<(), Self::Error> {
        w.write(&self.id)?;
        w.write(&self.tag)?;
        w.write(&self.active)?;
        Ok(())
    }
}

impl Decode for TestRecord {
    type Error = Error;

    fn decode(r: &mut Reader<'_, ()>) -> Result<Self, Self::Error> {
        Ok(Self { id: r.read()?, tag: r.read()?, active: r.read()? })
    }
}

#[test]
fn custom_struct_roundtrips_with_encode_and_decode() {
    let value = TestRecord { id: 0xBEEF, tag: 7, active: true };

    let mut writer = Writer::new(Endian::Big, ());
    writer.write(&value).unwrap();
    let bytes = writer.finish();
    assert_eq!(bytes, [0xBE, 0xEF, 0x07, 0x01]);

    let mut reader = Reader::new(&bytes, Endian::Big, ());
    let decoded: TestRecord = reader.read().unwrap();

    assert_eq!(decoded, value);
    assert!(reader.is_eof());
}

#[test]
fn custom_codec_encodes_decodes_and_surfaces_errors() {
    let mut writer = Writer::new(Endian::Big, ());
    InvertedBoolCodec::encode(&true, &mut writer).unwrap();
    assert_eq!(writer.finish(), [0x00]);

    let mut reader = Reader::new(&[0x01], Endian::Big, ());
    let decoded = InvertedBoolCodec::decode(&mut reader).unwrap();
    assert!(!decoded);

    let mut invalid = Reader::new(&[0xFF], Endian::Big, ());
    let invalid_err = InvertedBoolCodec::decode(&mut invalid).unwrap_err();
    assert!(matches!(invalid_err, InvertedBoolCodecError::InvalidDiscriminant(0xFF)));

    let mut truncated = Reader::new(&[], Endian::Big, ());
    let core_err = InvertedBoolCodec::decode(&mut truncated).unwrap_err();
    assert!(matches!(core_err, InvertedBoolCodecError::Core(Error::OutOfBounds)));
}