use crate::{
prelude::*,
ruby,
util::Sealed,
};
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()))
}
#[inline]
unsafe fn _attr(m: ruby::VALUE, name: SymbolId, read: bool, write: bool) {
ruby::rb_attr(m, name.raw(), read as _, write as _, 0);
}
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(
self,
superclass: Class,
name: impl Into<SymbolId>,
) -> Result<Class, DefMixinError> {
Class::_def_under(self, superclass, 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 Object) {
unsafe { ruby::rb_const_set(self.raw(), name.into().raw(), val.raw()) };
}
#[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 {
let t = unsafe { ruby::rb_cvar_defined(self.raw(), var.into().raw()) };
t == crate::util::TRUE_VALUE
}
#[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>,
{
crate::protected(|| unsafe { self.set_class_var_unchecked(key, val) })
}
#[inline]
unsafe fn set_class_var_unchecked<K, V>(self, key: K, val: V)
where
K: Into<SymbolId>,
V: Into<AnyObject>,
{
ruby::rb_cvar_set(self.raw(), key.into().raw(), val.into().raw());
}
#[inline]
fn attr_reader<N: Into<SymbolId>>(self, name: N) -> Result {
crate::protected(|| unsafe { self.attr_reader_unchecked(name) })
}
#[inline]
unsafe fn attr_reader_unchecked<N: Into<SymbolId>>(self, name: N) {
_attr(self.raw(), name.into(), true, false);
}
#[inline]
fn attr_writer<N: Into<SymbolId>>(self, name: N) -> Result {
crate::protected(|| unsafe { self.attr_writer_unchecked(name) })
}
#[inline]
unsafe fn attr_writer_unchecked<N: Into<SymbolId>>(self, name: N) {
_attr(self.raw(), name.into(), false, true);
}
#[inline]
fn attr_accessor<N: Into<SymbolId>>(self, name: N) -> Result {
crate::protected(|| unsafe { self.attr_accessor_unchecked(name) })
}
#[inline]
unsafe fn attr_accessor_unchecked<N: Into<SymbolId>>(self, name: N) {
_attr(self.raw(), name.into(), true, true);
}
#[inline]
unsafe fn eval_unchecked(self, args: impl EvalArgs) -> AnyObject {
args.eval_in_unchecked(self)
}
#[inline]
fn eval(self, args: impl EvalArgs) -> Result<AnyObject> {
args.eval_in(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)
}
}
pub trait EvalArgs: Sized {
#[inline]
fn eval_in(self, mixin: impl Mixin) -> Result<AnyObject> {
crate::protected(|| unsafe { self.eval_in_unchecked(mixin) })
}
unsafe fn eval_in_unchecked(self, mixin: impl Mixin) -> AnyObject;
}
impl<O: Object> EvalArgs for &[O] {
#[inline]
unsafe fn eval_in_unchecked(self, mixin: impl Mixin) -> AnyObject {
let raw = ruby::rb_mod_module_eval(
self.len() as _,
self.as_ptr() as *const ruby::VALUE,
mixin.raw(),
);
AnyObject::from_raw(raw)
}
}
impl EvalArgs for String {
#[inline]
unsafe fn eval_in_unchecked(self, mixin: impl Mixin) -> AnyObject {
self.as_any_slice().eval_in_unchecked(mixin)
}
}
impl EvalArgs for &str {
#[inline]
unsafe fn eval_in_unchecked(self, mixin: impl Mixin) -> AnyObject {
String::from(self).eval_in_unchecked(mixin)
}
}
impl<S: Into<String>, F: Into<String>> EvalArgs for (S, F) {
#[inline]
unsafe fn eval_in_unchecked(self, mixin: impl Mixin) -> AnyObject {
let (s, f) = self;
[s.into(), f.into()].eval_in_unchecked(mixin)
}
}
impl<S: Into<String>, F: Into<String>, L: Into<u32>> EvalArgs for (S, F, L) {
#[inline]
unsafe fn eval_in_unchecked(self, _mixin: impl Mixin) -> AnyObject {
unimplemented!("TODO: Convert u32 to object");
}
}
#[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 ruby::value_type::*;
use DefMixinError::*;
let existing = _get_const(m, name)?;
let raw = existing.raw();
let err = match crate::util::value_built_in_type(raw) {
Some(MODULE) => unsafe {
ExistingModule(Module::from_raw(raw))
},
Some(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,
}
}
}