use crate::{
prelude::*,
ruby::{self, ID, VALUE},
util::Sealed,
vm::EvalArgs,
};
mod class;
mod method;
mod module;
pub use self::{class::*, method::*, module::*};
#[inline]
fn _get_const(m: impl Mixin, name: SymbolId) -> Option<AnyObject> {
unsafe {
if ruby::rb_const_defined(m.raw(), name.raw()) != 0 {
Some(_get_const_unchecked(m, name))
} else {
None
}
}
}
#[inline]
unsafe fn _get_const_unchecked(m: impl Mixin, name: impl Into<SymbolId>) -> AnyObject {
AnyObject::from_raw(ruby::rb_const_get(m.raw(), name.into().raw()))
}
unsafe fn _set_attr(m: VALUE, name: ID, read: bool, write: bool) -> Result {
crate::protected_no_panic(|| _set_attr_unchecked(m, name, read, write))
}
#[inline]
unsafe fn _set_attr_unchecked(m: VALUE, name: ID, read: bool, write: bool) {
ruby::rb_attr(m, name, read as _, write as _, 0);
}
unsafe fn _set_class_var(m: VALUE, key: ID, val: VALUE) -> Result {
crate::protected_no_panic(|| _set_class_var_unchecked(m, key, val))
}
#[inline]
unsafe fn _set_class_var_unchecked(m: VALUE, key: ID, val: VALUE) {
ruby::rb_cvar_set(m, key, val)
}
pub trait Mixin: Object + Sealed {
fn to_class(self) -> Result<Class, Module>;
fn to_module(self) -> Result<Module, Class>;
#[inline]
fn include(self, module: Module) {
unsafe { ruby::rb_include_module(self.raw(), module.raw()) };
}
#[inline]
#[must_use]
fn includes(self, module: Module) -> bool {
unsafe { ruby::rb_mod_include_p(self.raw(), module.raw()) != 0 }
}
#[inline]
fn included_modules(self) -> Array<Module> {
unsafe { Array::from_raw(ruby::rb_mod_included_modules(self.raw())) }
}
#[inline]
fn prepend(self, module: Module) {
unsafe { ruby::rb_prepend_module(self.raw(), module.raw()) };
}
#[inline]
fn def_class(
self,
name: impl Into<SymbolId>,
) -> Result<Class, DefMixinError> {
Class::_def_under(self, Class::object(), name.into())
}
#[inline]
fn def_subclass<S: Object>(
self,
superclass: Class<S>,
name: impl Into<SymbolId>,
) -> Result<Class, DefMixinError> {
Class::_def_under(self, superclass.into_any_class(), name.into())
}
#[inline]
fn get_class(
self,
name: impl Into<SymbolId>,
) -> Option<Class> {
_get_const(self, name.into())?.to_class()
}
#[inline]
unsafe fn get_class_unchecked(
self,
name: impl Into<SymbolId>,
) -> Class {
Class::cast_unchecked(_get_const_unchecked(self, name))
}
#[inline]
fn def_module(
self,
name: impl Into<SymbolId>,
) -> Result<Module, DefMixinError> {
Module::_def_under(self, name.into())
}
#[inline]
fn get_module(
self,
name: impl Into<SymbolId>,
) -> Option<Module> {
_get_const(self, name.into())?.to_module()
}
#[inline]
unsafe fn get_module_unchecked(
self,
name: impl Into<SymbolId>,
) -> Module {
Module::cast_unchecked(_get_const_unchecked(self, name))
}
#[inline]
fn has_const(self, name: impl Into<SymbolId>) -> bool {
unsafe { ruby::rb_const_defined(self.raw(), name.into().raw()) != 0 }
}
#[inline]
fn get_const(self, name: impl Into<SymbolId>) -> AnyObject {
let name = name.into().raw();
unsafe { AnyObject::from_raw(ruby::rb_const_get(self.raw(), name)) }
}
#[inline]
fn set_const(self, name: impl Into<SymbolId>, val: impl Into<AnyObject>) {
let val = val.into().raw();
unsafe { ruby::rb_const_set(self.raw(), name.into().raw(), val) };
}
#[inline]
fn remove_const(self, name: impl Into<SymbolId>) -> AnyObject {
let name = name.into().raw();
unsafe { AnyObject::from_raw(ruby::rb_const_remove(self.raw(), name)) }
}
#[inline]
fn has_class_var(self, var: impl Into<SymbolId>) -> bool {
unsafe { ruby::rb_cvar_defined(self.raw(), var.into().raw()) != 0 }
}
#[inline]
fn get_class_var(self, var: impl Into<SymbolId>) -> AnyObject {
let var = var.into().raw();
unsafe { AnyObject::from_raw(ruby::rb_cvar_get(self.raw(), var)) }
}
#[inline]
fn set_class_var<K, V>(self, key: K, val: V) -> Result
where
K: Into<SymbolId>,
V: Into<AnyObject>,
{
let key = key.into().raw();
let val = val.into().raw();
unsafe { _set_class_var(self.raw(), key, val) }
}
#[inline]
unsafe fn set_class_var_unchecked<K, V>(self, key: K, val: V)
where
K: Into<SymbolId>,
V: Into<AnyObject>,
{
_set_class_var_unchecked(self.raw(), key.into().raw(), val.into().raw())
}
#[inline]
fn def_attr_reader<N: Into<SymbolId>>(self, name: N) -> Result {
unsafe { _set_attr(self.raw(), name.into().raw(), true, false) }
}
#[inline]
unsafe fn def_attr_reader_unchecked<N: Into<SymbolId>>(self, name: N) {
_set_attr_unchecked(self.raw(), name.into().raw(), true, false);
}
#[inline]
fn def_attr_writer<N: Into<SymbolId>>(self, name: N) -> Result {
unsafe { _set_attr(self.raw(), name.into().raw(), false, true) }
}
#[inline]
unsafe fn def_attr_writer_unchecked<N: Into<SymbolId>>(self, name: N) {
_set_attr_unchecked(self.raw(), name.into().raw(), false, true);
}
#[inline]
fn def_attr_accessor<N: Into<SymbolId>>(self, name: N) -> Result {
unsafe { _set_attr(self.raw(), name.into().raw(), true, true) }
}
#[inline]
unsafe fn def_attr_accessor_unchecked<N: Into<SymbolId>>(self, name: N) {
_set_attr_unchecked(self.raw(), name.into().raw(), true, true);
}
#[inline]
unsafe fn eval(self, args: impl EvalArgs) -> AnyObject {
args.eval_in_mixin(self)
}
#[inline]
unsafe fn eval_protected(self, args: impl EvalArgs) -> Result<AnyObject> {
args.eval_in_mixin_protected(self)
}
}
impl Mixin for Class {
#[inline]
fn to_class(self) -> Result<Class, Module> {
Ok(self)
}
#[inline]
fn to_module(self) -> Result<Module, Class> {
Err(self)
}
}
impl Mixin for Module {
#[inline]
fn to_class(self) -> Result<Class, Module> {
Err(self)
}
#[inline]
fn to_module(self) -> Result<Module, Class> {
Ok(self)
}
}
#[derive(Debug)]
pub enum DefMixinError {
ExistingClass(Class),
ExistingModule(Module),
ExistingConst(AnyObject),
FrozenClass(Class),
FrozenModule(Module),
}
impl DefMixinError {
#[cold]
#[inline]
pub(crate) fn _frozen(m: impl Mixin) -> Self {
match m.to_class() {
Ok(class) => DefMixinError::FrozenClass(class),
Err(module) => DefMixinError::FrozenModule(module),
}
}
#[inline]
fn _get(m: impl Mixin, name: SymbolId) -> Option<Self> {
use crate::object::Ty;
use DefMixinError::*;
let existing = _get_const(m, name)?;
let raw = existing.raw();
let err = match crate::util::value_built_in_ty(raw) {
Some(Ty::MODULE) => unsafe {
ExistingModule(Module::from_raw(raw))
},
Some(Ty::CLASS) => unsafe {
ExistingClass(Class::from_raw(raw))
},
Some(_) | None => ExistingConst(existing),
};
Some(err)
}
#[inline]
pub fn existing_class(&self) -> Option<Class> {
match *self {
DefMixinError::ExistingClass(c) => Some(c),
_ => None,
}
}
#[inline]
pub fn existing_module(&self) -> Option<Module> {
match *self {
DefMixinError::ExistingModule(m) => Some(m),
_ => None,
}
}
#[inline]
pub fn existing_const(&self) -> Option<AnyObject> {
match *self {
DefMixinError::ExistingConst(m) => Some(m),
_ => None,
}
}
#[inline]
pub fn existing_object(&self) -> Option<AnyObject> {
use DefMixinError::*;
match *self {
ExistingModule(m) => Some(m.into_any_object()),
ExistingClass(c) => Some(c.into_any_object()),
ExistingConst(c) => Some(c),
_ => None,
}
}
}