use super::Dict;
use crate::{Accept, Context, RefValue, Reject};
use num_bigint::BigInt;
use std::any::Any;
pub type BoxedObject = Box<dyn Object>;
pub trait AnyBoxedObject {
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any>;
}
impl<T> AnyBoxedObject for T
where
T: 'static + Object,
{
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
pub trait CloneBoxedObject {
fn dyn_clone(&self) -> BoxedObject;
}
impl<T> CloneBoxedObject for T
where
T: 'static + Object + Clone,
{
fn dyn_clone(&self) -> BoxedObject {
Box::new(self.clone())
}
}
impl Clone for BoxedObject {
fn clone(&self) -> Self {
self.dyn_clone()
}
}
pub trait PartialEqBoxedObject {
fn dyn_eq(&self, other: &BoxedObject) -> bool;
}
impl<T> PartialEqBoxedObject for T
where
T: 'static + Object + PartialEq,
{
fn dyn_eq(&self, other: &BoxedObject) -> bool {
if let Some(other) = other.as_any().downcast_ref::<T>() {
self.eq(other)
} else {
false
}
}
}
impl PartialEq for BoxedObject {
fn eq(&self, other: &Self) -> bool {
self.dyn_eq(other)
}
}
impl PartialEq<&Self> for BoxedObject {
fn eq(&self, other: &&Self) -> bool {
self.dyn_eq(other)
}
}
impl Eq for BoxedObject {}
pub trait PartialOrdBoxedObject {
fn dyn_partial_cmp(&self, other: &BoxedObject) -> Option<std::cmp::Ordering>;
}
impl<T> PartialOrdBoxedObject for T
where
T: 'static + Object + PartialEq + PartialOrd,
{
fn dyn_partial_cmp(&self, other: &BoxedObject) -> Option<std::cmp::Ordering> {
if let Some(other) = other.as_any().downcast_ref::<T>() {
self.partial_cmp(other)
} else {
None
}
}
}
impl PartialOrd for BoxedObject {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.dyn_partial_cmp(other)
}
}
pub trait Object:
AnyBoxedObject
+ CloneBoxedObject
+ PartialEqBoxedObject
+ PartialOrdBoxedObject
+ std::any::Any
+ std::fmt::Debug {
fn id(&self) -> usize {
self as *const Self as *const () as usize
}
fn severity(&self) -> u8 {
0
}
fn name(&self) -> &'static str;
fn is(&self, name: &str) -> bool {
self.name() == name
}
fn repr(&self) -> String {
format!("<{} {:p}>", self.name(), self)
}
fn is_void(&self) -> bool {
false }
fn is_true(&self) -> bool {
true }
fn to_i64(&self) -> Result<i64, String> {
Err(format!("`{}` cannot be converted to i64", self.name()))
}
fn to_f64(&self) -> Result<f64, String> {
Err(format!("`{}` cannot be converted to f64", self.name()))
}
fn to_usize(&self) -> Result<usize, String> {
Err(format!("`{}` cannot be converted to usize", self.name()))
}
fn to_string(&self) -> String {
self.repr()
}
fn to_bigint(&self) -> Result<BigInt, String> {
Err(format!("`{}` cannot be converted to BigInt", self.name()))
}
fn is_callable(&self, _without_arguments: bool) -> bool {
false
}
fn is_consuming(&self) -> bool {
false
}
fn is_nullable(&self) -> bool {
false
}
fn is_mutable(&self) -> bool {
false
}
fn is_hashable(&self) -> bool {
!self.is_mutable()
}
fn call(
&self,
_context: Option<&mut Context>,
_args: Vec<RefValue>,
_nargs: Option<Dict>,
) -> Result<Accept, Reject> {
Err(format!("'{}' is not callable", self.name()).into())
}
fn call_direct(
&self,
context: &mut Context,
args: usize,
nargs: Option<Dict>,
) -> Result<Accept, Reject> {
let args = context.drain(args);
self.call(Some(context), args, nargs)
}
}