scriptful 0.4.0

A minimalist, `no_std` stack machine library for interpreting domain specific interpreted languages.
Documentation
use alloc::string::String;
use alloc::vec::Vec;

use crate::{
    core::value::Value,
    encoding::{
        codecs::simple::SimpleScriptCodec,
        dec::{Decode, Decoder},
        DecodingError,
    },
    op_systems::simple_math::MathOperator,
    prelude::*,
};

impl<'a> Decoder for &'a mut SimpleScriptCodec {
    type Error = DecodingError;

    fn decode_i128(&mut self) -> Result<i128, Self::Error> {
        let length = self.read_byte()? as usize - 0x02;
        let significant_bytes = self.read_bytes(length)?;
        let mut sixteen_bytes = [0u8; 16];
        sixteen_bytes[..length].copy_from_slice(&significant_bytes);
        let integer = i128::from_le_bytes(sixteen_bytes);

        Ok(integer)
    }

    fn decode_f64(&mut self) -> Result<f64, Self::Error> {
        self.read_byte()?;
        let bytes = self.read_bytes(8)?;
        let mut eight_bytes = [0u8; 8];
        eight_bytes.copy_from_slice(&bytes);
        let float = f64::from_le_bytes(eight_bytes);

        Ok(float)
    }

    fn decode_string(&mut self) -> Result<String, Self::Error> {
        let length_length = self.read_byte()? as usize - 0x13;
        let length_bytes = self.read_bytes(length_length)?;
        let mut eight_length_bytes = [0u8; 8];
        eight_length_bytes[..length_length].copy_from_slice(&length_bytes);
        let length = usize::from_le_bytes(eight_length_bytes);
        let bytes = self.read_bytes(length)?;
        let string = String::from_utf8(bytes.into())
            .map_err(|_| DecodingError::from_str("Not a valid UTF-8 string"));

        string
    }

    fn decode_item<Op, Val>(&mut self) -> Result<Item<Op, Val>, Self::Error>
    where
        Op: core::fmt::Debug + Decode,
        Val: core::fmt::Debug + Decode,
    {
        let byte = self.peek_byte()?;
        if *byte < 0x80 {
            Val::decode(&mut *self).map(Item::Value)
        } else {
            Op::decode(&mut *self).map(Item::Operator)
        }
    }

    fn decode_script<Op, Val>(&mut self) -> Result<Script<Op, Val>, Self::Error>
    where
        Op: core::fmt::Debug + Decode,
        Val: core::fmt::Debug + Decode,
    {
        let mut script = Script::<Op, Val>::new();

        while self.bytes_left() > 0 {
            let item = self.decode_item().unwrap();
            script.push(item);
        }

        Ok(script)
    }

    fn from_vec<Op, Val>(input: Vec<u8>) -> Result<Script<Op, Val>, Self::Error>
    where
        Op: core::fmt::Debug + Decode,
        Val: core::fmt::Debug + Decode,
    {
        let mut codec = SimpleScriptCodec::from_data(input);
        let script = <&mut SimpleScriptCodec as Decoder>::decode_script(&mut &mut codec);

        script
    }

    fn peek_byte(&self) -> Result<&u8, Self::Error> {
        self.data
            .get(self.cursor)
            .ok_or_else(|| DecodingError::from_str("Decoder cursor hit end of vector"))
    }

    fn read_byte(&mut self) -> Result<u8, Self::Error> {
        if self.cursor < self.data.len() {
            let byte = self.data[self.cursor];
            self.cursor += 1;

            Ok(byte)
        } else {
            Err(DecodingError::from_str(
                "Decoder cursor hit end of vector when reading a single byte",
            ))
        }
    }

    fn read_bytes(&mut self, length: usize) -> Result<&[u8], Self::Error> {
        if self.cursor + length <= self.data.len() {
            let bytes = &self.data[self.cursor..self.cursor + length];
            self.cursor += length;

            Ok(bytes)
        } else {
            Err(DecodingError::from_str(&alloc::format!(
                "Decoder cursor hit end of vector when reading {} bytes, while the decoder only had {} in its data vector",
                length, self.bytes_left()
            )))
        }
    }
}

impl Decode for MathOperator {
    fn decode<D>(decoder: &mut D) -> Result<Self, <D as Decoder>::Error>
    where
        D: Decoder,
    {
        let discriminant = decoder.read_byte()? - 0x80;

        match discriminant {
            0x00 => Ok(MathOperator::Add),
            0x01 => Ok(MathOperator::Equal),
            0x02 => Ok(MathOperator::Mul),
            0x03 => Ok(MathOperator::Not),
            0x04 => Ok(MathOperator::Sub),
            x => Err(<D as Decoder>::Error::from_str(&alloc::format!(
                "Unsupported MathOperator {}",
                x
            ))),
        }
    }
}

impl Decode for Value {
    fn decode<D>(decoder: &mut D) -> Result<Self, <D as Decoder>::Error>
    where
        D: Decoder,
    {
        let discriminant = decoder.peek_byte()?;

        match discriminant {
            0x00 => decoder.read_byte().map(|_| Value::Boolean(false)),
            0x01 => decoder.read_byte().map(|_| Value::Boolean(true)),
            0x02 => decoder.decode_f64().map(Value::Float),
            0x03..=0x012 => decoder.decode_i128().map(Value::Integer),
            0x13..=0x79 => decoder.decode_string().map(Value::String),
            x => Err(<D as Decoder>::Error::from_str(&alloc::format!(
                "Unsupported value discriminant {}",
                x
            ))),
        }
    }
}