use std::convert::From;
use crate::{
binding::{class, global::ValueType, vm},
typed_data::DataTypeWrapper,
types::{Callback, Value},
util,
};
use crate::{AnyException, AnyObject, Boolean, Class, Exception, NilClass, VerifiedObject, VM};
pub trait Object: From<Value> {
fn value(&self) -> Value;
fn class(&self) -> Class {
let class = class::object_class(self.value());
Class::from(class)
}
fn singleton_class(&self) -> Class {
let class = class::singleton_class(self.value());
Class::from(class)
}
fn get_data<'a, T>(&'a self, wrapper: &'a dyn DataTypeWrapper<T>) -> &T {
class::get_data(self.value(), wrapper)
}
fn get_data_mut<'a, T>(&'a mut self, wrapper: &'a dyn DataTypeWrapper<T>) -> &mut T {
class::get_data(self.value(), wrapper)
}
fn define<F: Fn(&mut Self)>(&mut self, f: F) -> &Self {
f(self);
self
}
fn define_method<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
class::define_method(self.value(), name, callback);
}
fn define_private_method<I: Object, O: Object>(
&mut self,
name: &str,
callback: Callback<I, O>,
) {
class::define_private_method(self.value(), name, callback);
}
fn define_singleton_method<I: Object, O: Object>(
&mut self,
name: &str,
callback: Callback<I, O>,
) {
class::define_singleton_method(self.value(), name, callback);
}
fn def<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
self.define_method(name, callback);
}
fn def_private<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
self.define_private_method(name, callback);
}
fn def_self<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
self.define_singleton_method(name, callback);
}
unsafe fn send(&self, method: &str, arguments: &[AnyObject]) -> AnyObject {
let arguments = util::arguments_to_values(arguments);
let result = vm::call_method(self.value(), method, &arguments);
AnyObject::from(result)
}
fn equals<T: Object>(&self, other: &T) -> bool {
class::equals(self.value(), other.value()).is_true()
}
fn case_equals<T: Object>(&self, other: &T) -> bool {
let v = self.value();
let m = "===";
let a = [other.value()];
vm::call_method(v, m, &a).is_true()
}
fn is_eql<T: Object>(&self, other: &T) -> bool {
class::is_eql(self.value(), other.value()).is_true()
}
fn is_equal<T: Object>(&self, other: &T) -> bool {
self.value() == other.value()
}
fn respond_to(&self, method: &str) -> bool {
class::respond_to(self.value(), method)
}
fn protect_send(
&self,
method: &str,
arguments: &[AnyObject],
) -> Result<AnyObject, AnyException> {
let closure = || unsafe { self.send(&method, arguments.as_ref()) };
let result = VM::protect(closure);
result.map_err(|_| {
let output = VM::error_info().unwrap();
VM::clear_error_info();
output
})
}
fn protect_public_send(
&self,
method: &str,
arguments: &[AnyObject],
) -> Result<AnyObject, AnyException> {
let v = self.value();
let arguments = util::arguments_to_values(arguments);
let closure = || vm::call_public_method(v, &method, &arguments).into();
let result = VM::protect(closure);
result.map_err(|_| {
let output = VM::error_info().unwrap();
VM::clear_error_info();
output
})
}
fn is_nil(&self) -> bool {
self.value().is_nil()
}
fn to_any_object(&self) -> AnyObject {
AnyObject::from(self.value())
}
fn instance_variable_get(&self, variable: &str) -> AnyObject {
let result = class::instance_variable_get(self.value(), variable);
AnyObject::from(result)
}
fn instance_variable_set<T: Object>(&mut self, variable: &str, value: T) -> AnyObject {
let result = class::instance_variable_set(self.value(), variable, value.value());
AnyObject::from(result)
}
fn is_frozen(&self) -> bool {
self.value().is_frozen()
}
fn freeze(&mut self) -> Self {
let result = class::freeze(self.value());
Self::from(result)
}
unsafe fn to<T: Object>(&self) -> T {
T::from(self.value())
}
fn try_convert_to<T: VerifiedObject>(&self) -> Result<T, AnyException> {
if T::is_correct_type(self) {
let converted_object = unsafe { self.to::<T>() };
Ok(converted_object)
} else {
Err(AnyException::new("TypeError", Some(T::error_message())))
}
}
fn ty(&self) -> ValueType {
self.value().ty()
}
}
impl<Obj: Object> Object for Option<Obj>
where
Option<Obj>: From<Value>,
{
fn value(&self) -> Value {
match self {
Some(val) => val.value(),
None => NilClass::new().into(),
}
}
}