reikland 0.2.0

A serde-compatible Ruby marshal parser and deserializer
Documentation
use crate::cursor::{Cursor, TryFromCursor};

#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum MarshalTypeByte {
    True = b'T',
    False = b'F',
    Nil = b'0',
    Fixnum = b'i',
    Symbol = b':',
    SymbolLink = b';',
    ObjectReference = b'@',
    Instance = b'I',
    Extended = b'e',
    Array = b'[',
    Bignum = b'l',
    Class = b'c',
    Module = b'm',
    Data = b'd',
    Float = b'f',
    Hash = b'{',
    HashDefault = b'}',
    Object = b'o',
    RegularExpression = b'/',
    String = b'"',
    Struct = b'S',
    UserString = b'C',
    UserDefined = b'u',
    UserMarshal = b'U',
    ClassOrModule = b'M',
}

impl std::fmt::Display for MarshalTypeByte {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (*self as u8 as char).fmt(f)
    }
}

impl MarshalTypeByte {
    pub const fn type_name(self) -> &'static str {
        match self {
            Self::True => "true",
            Self::False => "false",
            Self::Nil => "nil",
            Self::Fixnum => "fixnum",
            Self::Symbol => "symbol",
            Self::SymbolLink => "symbol_link",
            Self::ObjectReference => "object_ref",
            Self::Instance => "instance",
            Self::Extended => "extended",
            Self::Array => "array",
            Self::Bignum => "bignum",
            Self::Class => "class",
            Self::Module => "module",
            Self::Data => "data",
            Self::Float => "float",
            Self::Hash => "hash",
            Self::HashDefault => "hash_default",
            Self::Object => "object",
            Self::RegularExpression => "regex",
            Self::String => "string",
            Self::Struct => "struct",
            Self::UserString => "user_string",
            Self::UserDefined => "user_defined",
            Self::UserMarshal => "user_marshal",
            Self::ClassOrModule => "class_or_module",
        }
    }

    pub const fn try_from_u8(byte: u8) -> Option<Self> {
        match byte {
            b'T' => Some(Self::True),
            b'F' => Some(Self::False),
            b'0' => Some(Self::Nil),
            b'i' => Some(Self::Fixnum),
            b':' => Some(Self::Symbol),
            b';' => Some(Self::SymbolLink),
            b'@' => Some(Self::ObjectReference),
            b'I' => Some(Self::Instance),
            b'e' => Some(Self::Extended),
            b'[' => Some(Self::Array),
            b'l' => Some(Self::Bignum),
            b'c' => Some(Self::Class),
            b'm' => Some(Self::Module),
            b'd' => Some(Self::Data),
            b'f' => Some(Self::Float),
            b'{' => Some(Self::Hash),
            b'}' => Some(Self::HashDefault),
            b'o' => Some(Self::Object),
            b'/' => Some(Self::RegularExpression),
            b'"' => Some(Self::String),
            b'S' => Some(Self::Struct),
            b'C' => Some(Self::UserString),
            b'u' => Some(Self::UserDefined),
            b'U' => Some(Self::UserMarshal),
            b'M' => Some(Self::ClassOrModule),
            _ => None,
        }
    }
}

impl TryFrom<u8> for MarshalTypeByte {
    type Error = InvalidTypeByte;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        MarshalTypeByte::try_from_u8(value).ok_or(InvalidTypeByte { byte: value })
    }
}

#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("{} is not a valid marshal type byte", *byte as char)]
pub struct InvalidTypeByte {
    byte: u8,
}

impl TryFromCursor<'_> for MarshalTypeByte {
    type Error = InvalidTypeByte;

    fn try_from_cursor(cursor: &mut Cursor<'_>) -> Option<Result<Self, Self::Error>> {
        cursor.take_1().map(TryFrom::try_from)
    }
}