rulox_types 0.6.1

Helper types for the rulox crate.
Documentation
#![allow(clippy::redundant_pub_crate)]
#![allow(clippy::unnecessary_wraps)]

use crate::functions::LoxArgs;
use crate::LoxError;
use crate::LoxObject;
use crate::LoxResult;
use crate::LoxValue;
use crate::MapKey;
use crate::Shared;

use std::collections::HashMap;

pub(super) fn is_bool(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Bool(_)
    )))
}

pub(super) fn is_str(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Str(_)
    )))
}

pub(super) fn is_num(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Num(_)
    )))
}

pub(super) fn is_arr(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Arr(_)
    )))
}

pub(super) fn is_function(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::BoundMethod(_, _) | LoxValue::Coroutine(_) | LoxValue::PrimitiveMethod(_, _)
    )))
}

pub(super) fn is_class(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Class(_)
    )))
}

pub(super) fn is_map(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Map(_)
    )))
}

pub(super) fn is_bytes(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Bytes(_)
    )))
}

pub(super) fn is_error(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(
        args.head.unwrap(),
        LoxValue::Error(_)
    )))
}

pub(super) fn is_nil(args: LoxArgs) -> LoxResult {
    Ok(LoxValue::Bool(matches!(args.head.unwrap(), LoxValue::Nil)))
}

macro_rules! default_collection {
    ( $name:ident, $inner:ty, $loxvalue_variant:ident, $( $index:tt )+ ) => {
        #[derive(Debug, Clone)]
        struct $name($inner, LoxValue);

        impl LoxObject for $name {
            fn type_name() -> String
            where
                Self: Sized,
            {
                stringify!($name).to_string()
            }

            fn get(
                &self,
                _this: Shared<dyn LoxObject>,
                key: &str,
            ) -> Result<LoxValue, Option<LoxError>> {
                Ok(LoxValue::$loxvalue_variant(self.0.clone()).get(key)?)
            }

            fn set(
                &mut self,
                _this: Shared<dyn LoxObject>,
                key: &str,
                value: LoxValue,
            ) -> Result<(), Option<LoxError>> {
                LoxValue::$loxvalue_variant(self.0.clone()).set(key, value)?;
                Ok(())
            }

            $( $index )+

            fn index_set(
                &mut self,
                key: LoxValue,
                value: LoxValue,
            ) -> Result<(), LoxError> {
                LoxValue::$loxvalue_variant(self.0.clone()).index_set(key, value)
            }
        }
    };
}

pub(super) fn set_default(mut args: LoxArgs) -> LoxResult {
    match args.head.clone().unwrap() {
        LoxValue::Arr(arr) => {
            default_collection!(
                DefaultArray,
                Shared<Vec<LoxValue>>,
                Arr,
                fn index(&self, key: LoxValue) -> Result<LoxValue, LoxError> {
                    self.0
                        .read()
                        .get(usize::try_from(key)?)
                        .map_or_else(|| self.1.call([].into()), |value| Ok(value.clone()))
                }
            );

            Ok(LoxValue::external(DefaultArray(
                arr,
                args.drain().next().unwrap(),
            )))
        }
        LoxValue::Map(map) => {
            default_collection!(
                DefaultMap,
                Shared<HashMap<MapKey, LoxValue>>,
                Map,
                fn index(&self, key: LoxValue) -> Result<LoxValue, LoxError> {
                    self.0
                        .read()
                        .get(&MapKey::verify_key(key)?)
                        .map_or_else(|| self.1.call([].into()), |value| Ok(value.clone()))
                }
            );

            Ok(LoxValue::external(DefaultMap(
                map,
                args.drain().next().unwrap(),
            )))
        }
        _ => Err(LoxError::type_error(
            format!("'{}' has no attribute 'set_default'", args.head.unwrap()).into(),
        )),
    }
}