use std::{
fmt,
ffi::{c_void, CStr, CString},
marker::PhantomData,
};
use crate::{
object::Ty,
prelude::*,
ruby,
};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct AnyObject {
raw: ruby::VALUE,
_marker: PhantomData<*const c_void>,
}
impl AsRef<AnyObject> for AnyObject {
#[inline]
fn as_ref(&self) -> &Self { self }
}
unsafe impl Object for AnyObject {
#[inline]
fn unique_id() -> Option<u128> {
Some(!0)
}
#[inline]
unsafe fn from_raw(raw: ruby::VALUE) -> Self {
AnyObject { raw, _marker: PhantomData }
}
#[inline]
fn cast<A: Object>(obj: A) -> Option<Self> {
Some(obj.into_any_object())
}
fn ty(self) -> Ty {
crate::util::value_ty(self.raw())
}
#[inline]
fn raw(self) -> ruby::VALUE {
self.raw
}
#[inline]
fn as_any_object(&self) -> &Self { &self }
#[inline]
fn into_any_object(self) -> Self { self }
}
impl<O: Object> PartialEq<O> for AnyObject {
#[inline]
fn eq(&self, other: &O) -> bool {
let result = unsafe { self.call_with("==", &[*other]) };
result.raw() == crate::util::TRUE_VALUE
}
}
macro_rules! impl_eq {
($($t:ty, $convert:ident;)+) => { $(
impl PartialEq<$t> for AnyObject {
#[inline]
fn eq(&self, other: &$t) -> bool {
if let Some(value) = AnyObject::$convert(*self) {
value == *other
} else {
false
}
}
}
impl PartialEq<&$t> for AnyObject {
#[inline]
fn eq(&self, other: &&$t) -> bool {
*self == **other
}
}
impl PartialEq<AnyObject> for $t {
#[inline]
fn eq(&self, obj: &AnyObject) -> bool {
obj == self
}
}
impl PartialEq<AnyObject> for &$t {
#[inline]
fn eq(&self, obj: &AnyObject) -> bool {
obj == self
}
}
)+ }
}
impl_eq! {
str, to_string;
std::string::String, to_string;
CStr, to_string;
CString, to_string;
bool, to_bool;
}
impl Eq for AnyObject {}
impl fmt::Debug for AnyObject {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.inspect(), f)
}
}
impl fmt::Display for AnyObject {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.to_s(), f)
}
}
impl<O: Into<AnyObject>> From<Option<O>> for AnyObject {
#[inline]
fn from(option: Option<O>) -> Self {
option.map(Into::into).unwrap_or(AnyObject::nil())
}
}
impl<O: Into<AnyObject>, E: Into<AnyObject>> From<Result<O, E>> for AnyObject {
#[inline]
fn from(result: Result<O, E>) -> Self {
match result {
Ok(obj) => obj.into(),
Err(err) => err.into(),
}
}
}
impl From<bool> for AnyObject {
#[inline]
fn from(b: bool) -> Self {
Self::from_bool(b)
}
}
impl From<()> for AnyObject {
#[inline]
fn from(_nil: ()) -> Self {
Self::nil()
}
}
impl AnyObject {
#[inline]
pub(crate) fn _ptr(self) -> *mut std::ffi::c_void {
self.raw() as usize as _
}
#[inline]
pub fn convert_slice(objects: &[impl Object]) -> &[AnyObject] {
unsafe { &*(objects as *const [_] as *const _) }
}
#[inline]
pub fn call_super() -> Result<AnyObject> {
let args: &[AnyObject] = &[];
Self::call_super_with(args)
}
#[inline]
pub unsafe fn call_super_unchecked() -> AnyObject {
let args: &[AnyObject] = &[];
Self::call_super_with_unchecked(args)
}
#[inline]
pub fn call_super_with(args: &[impl Object]) -> Result<AnyObject> {
Self::_call_super_with(Self::convert_slice(args))
}
fn _call_super_with(args: &[AnyObject]) -> Result<AnyObject> {
unsafe {
crate::protected_no_panic(|| Self::call_super_with_unchecked(args))
}
}
#[inline]
pub unsafe fn call_super_with_unchecked(args: &[impl Object]) -> AnyObject {
let len = args.len();
let ptr = args.as_ptr() as *const ruby::VALUE;
AnyObject::from_raw(ruby::rb_call_super(len as _, ptr))
}
#[inline]
pub const unsafe fn from_raw(raw: ruby::VALUE) -> AnyObject {
AnyObject { raw, _marker: PhantomData }
}
#[inline]
pub const fn nil() -> AnyObject {
unsafe { AnyObject::from_raw(crate::util::NIL_VALUE) }
}
#[inline]
pub const fn from_bool(b: bool) -> AnyObject {
let raw = crate::util::TRUE_VALUE * b as ruby::VALUE;
unsafe { AnyObject::from_raw(raw) }
}
#[inline]
pub const fn is_nil(self) -> bool {
self.raw == crate::util::NIL_VALUE
}
#[inline]
pub const fn is_undefined(self) -> bool {
self.raw == crate::util::UNDEF_VALUE
}
#[inline]
pub const fn is_true(self) -> bool {
self.raw == crate::util::TRUE_VALUE
}
#[inline]
pub const fn is_false(self) -> bool {
self.raw == crate::util::FALSE_VALUE
}
#[inline]
pub fn to_bool(self) -> Option<bool> {
match self.raw() {
crate::util::TRUE_VALUE => Some(true),
crate::util::FALSE_VALUE => Some(false),
_ => None,
}
}
#[inline]
pub fn is_fixnum(self) -> bool {
crate::util::value_is_fixnum(self.raw())
}
#[inline]
pub fn is_bignum(self) -> bool {
self.ty() == Ty::BIGNUM
}
#[inline]
pub fn is_integer(self) -> bool {
self.is_fixnum() || self.is_bignum()
}
#[inline]
pub fn to_integer(self) -> Option<Integer> {
Integer::cast(self)
}
#[inline]
pub fn is_float(self) -> bool {
crate::util::value_is_float(self.raw())
}
#[inline]
pub fn to_float(self) -> Option<Float> {
Float::cast(self)
}
#[inline]
pub fn is_string(self) -> bool {
crate::util::value_is_built_in_ty(self.raw(), Ty::STRING)
}
#[inline]
pub fn to_string(self) -> Option<String> {
if self.is_string() {
unsafe { Some(String::cast_unchecked(self)) }
} else {
None
}
}
#[inline]
pub fn is_symbol(self) -> bool {
crate::util::value_is_sym(self.raw())
}
#[inline]
pub fn to_symbol(self) -> Option<Symbol> {
if self.is_symbol() {
unsafe { Some(Symbol::cast_unchecked(self)) }
} else {
None
}
}
#[inline]
pub fn is_array(self) -> bool {
crate::util::value_is_built_in_ty(self.raw(), Ty::ARRAY)
}
#[inline]
pub fn to_array(self) -> Option<Array> {
if self.is_array() {
unsafe { Some(Array::cast_unchecked(self)) }
} else {
None
}
}
#[inline]
pub fn is_class(self) -> bool {
crate::util::value_is_class(self.raw())
}
#[inline]
pub fn to_class(self) -> Option<Class> {
if self.is_class() {
unsafe { Some(Class::cast_unchecked(self)) }
} else {
None
}
}
#[inline]
pub fn is_module(self) -> bool {
crate::util::value_is_module(self.raw())
}
#[inline]
pub fn to_module(self) -> Option<Module> {
if self.is_module() {
unsafe { Some(Module::cast_unchecked(self)) }
} else {
None
}
}
#[inline]
pub fn is_exception(self) -> bool {
self.class().inherits(Class::exception())
}
#[inline]
pub fn to_exception(self) -> Option<AnyException> {
if self.is_exception() {
unsafe { Some(AnyException::cast_unchecked(self)) }
} else {
None
}
}
}