aiscript-vm 0.2.0

AIScript programming language interpreter
Documentation
use std::collections::HashMap;

use aiscript_arena::{Gc, RefLock};

use crate::{
    NativeFn, ReturnValue, Value,
    builtins::response,
    object::{Instance, Object},
};

use super::Vm;

impl Vm {
    pub fn get_global(&mut self, name: &'static str) -> Option<ReturnValue> {
        self.arena.mutate_root(|_mc, state| {
            let name = state.intern_static(name);
            state.globals.get(&name).copied().map(ReturnValue::from)
        })
    }

    pub fn register_extra_native_functions(&mut self) {
        self.arena.mutate_root(|_mc, state| {
            state.define_native_function("response", NativeFn(response::response));
            state.define_native_function(
                "temporary_redirect",
                NativeFn(response::temporary_redirect),
            );
            state.define_native_function(
                "permanent_redirect",
                NativeFn(response::permanent_redirect),
            );
        });
    }

    pub fn inject_sso_instance<K>(&mut self, fields: HashMap<K, serde_json::Value>)
    where
        K: AsRef<str> + Eq,
    {
        self.arena.mutate_root(|mc, state| {
            let ctx = state.get_context();
            let name = state.intern_static("sso");
            let class = crate::builtins::sso::create_sso_provider_class(ctx);
            let mut instance = Instance::new(class);
            for (key, value) in fields {
                instance.fields.insert(
                    state.intern(key.as_ref().as_bytes()),
                    Value::from_serde_value(ctx, &value),
                );
            }
            state
                .globals
                .insert(name, Gc::new(mc, RefLock::new(instance)).into());
        });
    }

    pub fn inject_variables(&mut self, variables: HashMap<String, serde_json::Value>) {
        self.arena.mutate_root(|_mc, state| {
            let ctx = state.get_context();
            for (key, value) in variables {
                let name = state.intern(key.as_bytes());
                state
                    .globals
                    .insert(name, Value::from_serde_value(ctx, &value));
            }
        });
    }

    pub fn inject_object<K>(&mut self, name: &'static str, fields: HashMap<K, serde_json::Value>)
    where
        K: AsRef<str> + Eq,
    {
        self.arena.mutate_root(|mc, state| {
            let ctx = state.get_context();
            let name = state.intern_static(name);
            let mut obj = Object::default();
            for (key, value) in fields {
                obj.fields.insert(
                    state.intern(key.as_ref().as_bytes()),
                    Value::from_serde_value(ctx, &value),
                );
            }
            state
                .globals
                .insert(name, Value::Object(Gc::new(mc, RefLock::new(obj))));
        });
    }
}

#[cfg(test)]
mod tests {
    use crate::ReturnValue;

    use super::*;

    #[test]
    fn test_inject_variables() {
        let mut vm = Vm::default();
        vm.inject_variables({
            let mut map = HashMap::new();
            map.insert("test".into(), "abc".into());
            map.insert("test2".into(), 123.into());
            map.insert("test3".into(), true.into());
            map
        });
        vm.compile("return test;").unwrap();
        let result = vm.interpret().unwrap();
        assert_eq!(result, ReturnValue::String("abc".into()));
        vm.compile("return test2;").unwrap();
        let result = vm.interpret().unwrap();
        assert_eq!(result, ReturnValue::Number(123.0));
        vm.compile("return test3;").unwrap();
        let result = vm.interpret().unwrap();
        assert_eq!(result, ReturnValue::Boolean(true));
    }

    #[test]
    fn test_inject_instance() {
        let mut vm = Vm::default();
        vm.inject_object("request", {
            let mut map = HashMap::new();
            map.insert("method", "get".into());
            map.insert("code", 200.0.into());
            map.insert("test", true.into());
            map
        });
        vm.compile("return request;").unwrap();
        let result = vm.interpret().unwrap();
        let request = result.as_object().unwrap();
        assert_eq!(request.get("method").unwrap(), "get");
        assert_eq!(request.get("code").unwrap(), 200.0);
        assert_eq!(request.get("test").unwrap(), true);
        assert!(request.get("abc").is_none());
    }
}