slim_protocol 0.2.0

General Slim Protocol stuff. Used internally by rust_slim and Temoc
Documentation
use crate::{InstructionResult, InstructionResultValue};

use super::Instruction;

#[derive(PartialEq, Eq, Debug)]
pub struct SlimString(String);

impl SlimString {
    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }
}

pub trait ToSlimString {
    fn to_slim_string(&self) -> SlimString;
}

impl<'a> ToSlimString for &'a str {
    fn to_slim_string(&self) -> SlimString {
        SlimString(format!("{:0>6}:{}", self.len(), self))
    }
}

impl ToSlimString for String {
    fn to_slim_string(&self) -> SlimString {
        self.as_str().to_slim_string()
    }
}

impl<T> ToSlimString for Vec<T>
where
    T: ToSlimString,
{
    fn to_slim_string(&self) -> SlimString {
        self.as_slice().to_slim_string()
    }
}

impl<'a, T> ToSlimString for &'a [T]
where
    T: ToSlimString,
{
    fn to_slim_string(&self) -> SlimString {
        let mut result = String::from("[");
        result += &format!("{:0>6}:", self.len());
        for value in self.iter() {
            result += &value.to_slim_string().0;
            result += ":";
        }
        result += "]";
        result.to_slim_string()
    }
}

impl<T, const S: usize> ToSlimString for [T; S]
where
    T: ToSlimString,
{
    fn to_slim_string(&self) -> SlimString {
        self.as_slice().to_slim_string()
    }
}

impl ToSlimString for Instruction {
    fn to_slim_string(&self) -> SlimString {
        match self {
            Self::Import { id, path } => [id.0.as_str(), "import", path.as_str()].to_slim_string(),
            Self::Make {
                id,
                instance,
                class,
                args,
            } => [
                [id.0.as_str(), "make", instance, class].as_slice(),
                into_vec_str(args).as_slice(),
            ]
            .concat()
            .to_slim_string(),
            Self::Call {
                id,
                instance,
                function,
                args,
            } => [
                [id.0.as_str(), "call", instance, function].as_slice(),
                into_vec_str(args).as_slice(),
            ]
            .concat()
            .to_slim_string(),
            Self::Assign { id, symbol, value } => {
                [id.0.as_str(), "assign", symbol, value].to_slim_string()
            }
            Self::CallAndAssign {
                id,
                symbol,
                instance,
                function,
                args,
            } => [
                [id.0.as_str(), "callAndAssign", symbol, instance, function].as_slice(),
                into_vec_str(args).as_slice(),
            ]
            .concat()
            .to_slim_string(),
        }
    }
}

impl ToSlimString for Box<dyn ToSlimString> {
    fn to_slim_string(&self) -> SlimString {
        self.as_ref().to_slim_string()
    }
}

impl ToSlimString for InstructionResult {
    fn to_slim_string(&self) -> SlimString {
        [
            Box::new(self.id.0.clone()) as Box<dyn ToSlimString>,
            Box::new(self.value.clone()) as Box<dyn ToSlimString>,
        ]
        .to_slim_string()
    }
}

impl ToSlimString for InstructionResultValue {
    fn to_slim_string(&self) -> SlimString {
        match self {
            InstructionResultValue::Ok => "OK".to_slim_string(),
            InstructionResultValue::Void => "/__VOID__/".to_slim_string(),
            InstructionResultValue::String(value) => value.as_str().to_slim_string(),
            InstructionResultValue::Exception(message) => {
                format!("__EXCEPTION__:{}", message.raw_message())
                    .as_str()
                    .to_slim_string()
            }
            InstructionResultValue::List(list) => list.to_slim_string(),
        }
    }
}

fn into_vec_str(args: &[String]) -> Vec<&str> {
    args.iter().map(|string| string.as_str()).collect()
}

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

    #[test]
    fn test_empty_string() {
        assert_eq!(SlimString("000000:".into()), "".to_slim_string());
    }

    #[test]
    fn test_hello_world() {
        assert_eq!(
            SlimString("000011:hello world".into()),
            "hello world".to_slim_string()
        );
    }

    #[test]
    fn test_string() {
        assert_eq!(
            SlimString("000011:hello world".into()),
            String::from("hello world").to_slim_string()
        );
    }

    #[test]
    fn test_empty_array() {
        let array: [String; 0] = [];
        assert_eq!(
            SlimString("000009:[000000:]".into()),
            array.to_slim_string()
        );
    }

    #[test]
    fn test_array() {
        assert_eq!(
            SlimString("000031:[000002:000003:one:000003:two:]".into()),
            ["one", "two"].to_slim_string()
        );
    }

    #[test]
    fn test_vec_and_slice() {
        let vec = vec!["one", "two"];
        assert_eq!(
            SlimString("000031:[000002:000003:one:000003:two:]".into()),
            vec.as_slice().to_slim_string()
        );
        assert_eq!(
            SlimString("000031:[000002:000003:one:000003:two:]".into()),
            vec.to_slim_string()
        );
    }

    #[test]
    fn test_instructions() {
        let id = Id::from("01HFM0NQM3ZS6BBX0ZH6VA6DJX");
        assert_eq!(
            SlimString("000865:[000008:000074:[000003:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000006:import:000009:some_path:]:000084:[000004:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000004:make:000008:instance:000005:Class:]:000108:[000006:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000004:make:000008:instance:000005:Class:000004:Arg1:000004:Arg2:]:000087:[000004:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000004:call:000008:instance:000008:function:]:000111:[000006:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000004:call:000008:instance:000008:function:000004:Arg1:000004:Arg2:]:000084:[000004:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000006:assign:000006:Symbol:000005:value:]:000110:[000005:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000013:callAndAssign:000006:symbol:000008:instance:000008:function:]:000134:[000007:000026:01HFM0NQM3ZS6BBX0ZH6VA6DJX:000013:callAndAssign:000006:symbol:000008:instance:000008:function:000004:Arg1:000004:Arg2:]:]".into()),
            [
                Instruction::Import {
                    id: id.clone(),
                    path: "some_path".into()
                },
                Instruction::Make {
                    id: id.clone(),
                    instance: "instance".into(),
                    class: "Class".into(),
                    args: vec![]
                },
                Instruction::Make {
                    id: id.clone(),
                    instance: "instance".into(),
                    class: "Class".into(),
                    args: vec!["Arg1".into(), "Arg2".into()]
                },
                Instruction::Call {
                    id: id.clone(),
                    instance: "instance".into(),
                    function: "function".into(),
                    args: vec![]
                },
                Instruction::Call {
                    id: id.clone(),
                    instance: "instance".into(),
                    function: "function".into(),
                    args: vec!["Arg1".into(), "Arg2".into()]
                },
                Instruction::Assign {
                    id: id.clone(),
                    symbol: "Symbol".into(),
                    value: "value".into()
                },
                Instruction::CallAndAssign {
                    id: id.clone(),
                    symbol: "symbol".into(),
                    instance: "instance".into(),
                    function: "function".into(),
                    args: vec![]
                },
                Instruction::CallAndAssign {
                    id: id.clone(),
                    symbol: "symbol".into(),
                    instance: "instance".into(),
                    function: "function".into(),
                    args: vec!["Arg1".into(), "Arg2".into()]
                }
            ]
            .to_slim_string()
        );
    }
}