use std::rc::Rc;
use crate::{
Error,
yamrb::{
helpers::{mrb_call_block, mrb_define_cmethod},
prelude::module::mrb_include_module,
value::{RObject, RValue},
vm::VM,
},
};
pub(crate) fn initialize_range(vm: &mut VM) {
let range_class = vm.define_standard_class("Range");
mrb_define_cmethod(
vm,
range_class.clone(),
"include?",
Box::new(mrb_range_is_include),
);
mrb_define_cmethod(vm, range_class.clone(), "each", Box::new(mrb_range_each));
let enumerable_module = vm.get_module_by_name("Enumerable");
mrb_include_module(&range_class, enumerable_module).expect("failed to include Enumerable");
}
pub fn mrb_range_is_include(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let this = vm.getself()?;
match &this.value {
RValue::Range(start, end, exclusive) => {
let obj = args[0].clone();
match (&start.value, &end.value, &obj.value) {
(RValue::Integer(start), RValue::Integer(end), RValue::Integer(obj)) => {
if *exclusive {
Ok(Rc::new(RObject::boolean(*start <= *obj && *obj < *end)))
} else {
Ok(Rc::new(RObject::boolean(*start <= *obj && *obj <= *end)))
}
}
(RValue::Integer(start), RValue::Integer(end), RValue::Float(obj)) => {
let obj = *obj as i64;
if *exclusive {
Ok(Rc::new(RObject::boolean(*start <= obj && obj < *end)))
} else {
Ok(Rc::new(RObject::boolean(*start <= obj && obj <= *end)))
}
}
_ => Ok(Rc::new(RObject::boolean(false))),
}
}
_ => Err(Error::RuntimeError(
"Range#include? must be called on a Range".to_string(),
)),
}
}
pub fn mrb_range_each(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let this = vm.getself()?;
let block = &args[0];
match &this.value {
RValue::Range(start, end, exclusive) => match (&start.value, &end.value) {
(RValue::Integer(start), RValue::Integer(end)) => {
let start = *start;
let mut end = *end;
if *exclusive {
end -= 1;
}
for i in start..=end {
let args = vec![Rc::new(RObject::integer(i))];
mrb_call_block(vm, block.clone(), None, &args, 0)?;
}
}
_ => {
return Err(Error::RuntimeError(
"Range#each must be called on a integer Range with block (for now)".to_string(),
));
}
},
_ => {
return Err(Error::RuntimeError(
"Range#each must be called on a Range".to_string(),
));
}
}
Ok(this.clone())
}