use std::ffi::CStr;
use crate::{
binding::symbol,
helpers::scan_args::ScanArgsFormat,
rubysys::{class, typed_data, types::RBasic},
typed_data::DataTypeWrapper,
types::{c_int, c_void, Callback, CallbackPtr, Id, Value, ValueType},
util, Object,
};
pub fn define_class(name: &str, superclass: Value) -> Value {
let name = util::str_to_cstring(name);
unsafe { class::rb_define_class(name.as_ptr(), superclass) }
}
pub fn define_nested_class(outer: Value, name: &str, superclass: Value) -> Value {
let name = util::str_to_cstring(name);
unsafe { class::rb_define_class_under(outer, name.as_ptr(), superclass) }
}
pub fn const_get(klass: Value, name: &str) -> Value {
unsafe { class::rb_const_get(klass, symbol::internal_id(name)) }
}
pub fn const_set(klass: Value, name: &str, value: Value) {
let name = util::str_to_cstring(name);
unsafe { class::rb_define_const(klass, name.as_ptr(), value) };
}
pub fn object_class(object: Value) -> Value {
unsafe { class::rb_obj_class(object) }
}
pub fn superclass(klass: Value) -> Value {
unsafe { class::rb_class_superclass(klass) }
}
pub fn singleton_class(object: Value) -> Value {
unsafe { class::rb_singleton_class(object) }
}
pub fn ancestors(klass: Value) -> Value {
unsafe { class::rb_mod_ancestors(klass) }
}
pub fn new_instance(klass: Value, arguments: &[Value]) -> Value {
let (argc, argv) = util::process_arguments(arguments);
unsafe { class::rb_class_new_instance(argc, argv, klass) }
}
pub fn instance_variable_get(object: Value, name: &str) -> Value {
unsafe { class::rb_ivar_get(object, symbol::internal_id(name)) }
}
pub fn instance_variable_set(object: Value, name: &str, value: Value) -> Value {
unsafe { class::rb_ivar_set(object, symbol::internal_id(name), value) }
}
pub fn define_attribute(object: Value, name: &str, reader: bool, writer: bool) {
let name = util::str_to_cstring(name);
let reader = util::bool_to_c_int(reader);
let writer = util::bool_to_c_int(writer);
unsafe { class::rb_define_attr(object, name.as_ptr(), reader, writer) };
}
pub fn respond_to(object: Value, method: &str) -> bool {
let result = unsafe { class::rb_respond_to(object, symbol::internal_id(method)) };
util::c_int_to_bool(result)
}
pub fn define_method<I: Object, O: Object>(klass: Value, name: &str, callback: Callback<I, O>) {
let name = util::str_to_cstring(name);
unsafe {
class::rb_define_method(klass, name.as_ptr(), callback as CallbackPtr, -1);
}
}
pub fn define_private_method<I: Object, O: Object>(
klass: Value,
name: &str,
callback: Callback<I, O>,
) {
let name = util::str_to_cstring(name);
unsafe {
class::rb_define_private_method(klass, name.as_ptr(), callback as CallbackPtr, -1);
}
}
pub fn define_singleton_method<I: Object, O: Object>(
klass: Value,
name: &str,
callback: Callback<I, O>,
) {
let name = util::str_to_cstring(name);
unsafe {
class::rb_define_singleton_method(klass, name.as_ptr(), callback as CallbackPtr, -1);
}
}
pub fn wrap_data<T>(klass: Value, data: T, wrapper: &dyn DataTypeWrapper<T>) -> Value {
let data = Box::into_raw(Box::new(data)) as *mut c_void;
unsafe { typed_data::rb_data_typed_object_wrap(klass, data, wrapper.data_type()) }
}
pub fn get_data<T>(object: Value, wrapper: &dyn DataTypeWrapper<T>) -> &mut T {
unsafe {
let data = typed_data::rb_check_typeddata(object, wrapper.data_type());
&mut *(data as *mut T)
}
}
pub fn is_frozen(object: Value) -> Value {
unsafe { class::rb_obj_frozen_p(object) }
}
pub fn freeze(object: Value) -> Value {
unsafe { class::rb_obj_freeze(object) }
}
pub fn is_eql(object1: Value, object2: Value) -> bool {
util::c_int_to_bool(unsafe { class::rb_eql(object1, object2) })
}
pub fn equals(object1: Value, object2: Value) -> Value {
unsafe { class::rb_equal(object1, object2) }
}
pub fn is_kind_of(object: Value, klass: Value) -> bool {
unsafe { class::rb_obj_is_kind_of(object, klass) }.is_true()
}
pub fn define_alias(klass: Value, new_name: &str, old_name: &str) {
let new_name = util::str_to_cstring(new_name);
let old_name = util::str_to_cstring(old_name);
unsafe { class::rb_define_alias(klass, new_name.as_ptr(), old_name.as_ptr()) };
}
pub fn undef_method(klass: Value, name: &str) {
let name = util::str_to_cstring(name);
unsafe { class::rb_undef_method(klass, name.as_ptr()) };
}
pub fn define_alloc_func(klass: Value, func: class::AllocFunction) {
unsafe { class::rb_define_alloc_func(klass, func) };
}
pub fn undef_alloc_func(klass: Value) {
unsafe { class::rb_undef_alloc_func(klass) };
}
pub fn get_kwargs(
keyword_hash: Value,
table: &[Id],
required: usize,
allow_extra: bool,
values: &mut [Value],
) {
assert!(required <= table.len() && values.len() >= table.len());
let optional = (table.len() - required) as c_int;
let optional = if allow_extra { -1 - optional } else { optional };
unsafe {
class::rb_get_kwargs(
keyword_hash,
table.as_ptr(),
required as c_int,
optional,
values.as_mut_ptr(),
)
};
}
pub const SCAN_ARGS_MAX_VARIABLES: usize = 30;
pub const SCAN_ARGS_PASS_CALLED_KEYWORDS: c_int = 0;
pub const SCAN_ARGS_LAST_HASH_KEYWORDS: c_int = 3;
pub fn scan_args(
arguments: &[Value],
format: &CStr,
out: &mut [Value; SCAN_ARGS_MAX_VARIABLES],
kw_flag: c_int,
) -> c_int {
let valid = format
.to_str()
.map(|format| ScanArgsFormat::parse(format).is_ok())
.unwrap_or(false);
assert!(valid, "invalid scan args format");
let (argc, argv) = util::process_arguments(arguments);
let o = out.as_mut_ptr();
unsafe {
class::rb_scan_args_kw(
kw_flag,
argc,
argv,
format.as_ptr(),
o,
o.add(1),
o.add(2),
o.add(3),
o.add(4),
o.add(5),
o.add(6),
o.add(7),
o.add(8),
o.add(9),
o.add(10),
o.add(11),
o.add(12),
o.add(13),
o.add(14),
o.add(15),
o.add(16),
o.add(17),
o.add(18),
o.add(19),
o.add(20),
o.add(21),
o.add(22),
o.add(23),
o.add(24),
o.add(25),
o.add(26),
o.add(27),
o.add(28),
o.add(29),
)
}
}
pub fn is_method_defined(klass: Value, name: &str, include_private: bool) -> bool {
let ex = if include_private { 0 } else { 1 };
unsafe { class::rb_method_boundp(klass, symbol::internal_id(name), ex) != 0 }
}
pub fn class_name(klass: Value) -> Value {
unsafe { class::rb_class_name(klass) }
}
pub fn class_path(klass: Value) -> Value {
unsafe { class::rb_class_path(klass) }
}
pub fn module_name(module: Value) -> Value {
unsafe { class::rb_mod_name(module) }
}
pub fn inherited_p(module: Value, other: Value) -> Value {
unsafe { class::rb_class_inherited_p(module, other) }
}
pub fn include_p(module: Value, other: Value) -> bool {
unsafe { class::rb_mod_include_p(module, other) }.is_true()
}
pub fn module_eval(module: Value, code: &str) -> Value {
let arguments = [crate::binding::string::new_utf8(code)];
let (argc, argv) = util::process_arguments(&arguments);
unsafe { class::rb_mod_module_eval(argc, argv, module) }
}
pub fn instance_methods(module: Value, include_inherited: bool) -> Value {
let arguments = [util::bool_to_value(include_inherited)];
let (argc, argv) = util::process_arguments(&arguments);
unsafe { class::rb_class_instance_methods(argc, argv, module) }
}
pub fn is_class_variable_defined(klass: Value, name: &str) -> bool {
unsafe { class::rb_cvar_defined(klass, symbol::internal_id(name)) }.is_true()
}
pub fn class_variable_get(klass: Value, name: &str) -> Value {
unsafe { class::rb_cvar_get(klass, symbol::internal_id(name)) }
}
pub fn class_variable_set(klass: Value, name: &str, value: Value) {
unsafe { class::rb_cvar_set(klass, symbol::internal_id(name), value) }
}
pub fn is_const_defined(klass: Value, name: &str) -> bool {
util::c_int_to_bool(unsafe { class::rb_const_defined(klass, symbol::internal_id(name)) })
}
pub fn is_const_defined_at(klass: Value, name: &str) -> bool {
util::c_int_to_bool(unsafe { class::rb_const_defined_at(klass, symbol::internal_id(name)) })
}
pub fn const_remove(module: Value, name: &str) -> Value {
unsafe { class::rb_const_remove(module, symbol::internal_id(name)) }
}
pub fn path_to_class(path: &CStr) -> Value {
unsafe { class::rb_path2class(path.as_ptr()) }
}
pub fn define_global_const(name: &str, value: Value) {
let name = util::str_to_cstring(name);
unsafe { class::rb_define_global_const(name.as_ptr(), value) }
}
pub fn subclasses(klass: Value) -> Value {
unsafe { class::rb_class_subclasses(klass) }
}
pub fn attached_object(klass: Value) -> Value {
unsafe { class::rb_class_attached_object(klass) }
}
pub fn refinement_new() -> Value {
unsafe { class::rb_refinement_new() }
}
pub fn class_variable_find(klass: Value, name: &str) -> (Value, Value) {
let mut front = Value::from(0);
let value = unsafe { class::rb_cvar_find(klass, symbol::internal_id(name), &mut front) };
if front.ty() == ValueType::IClass {
front = Value::from(unsafe { (*(front.value as *const RBasic)).klass });
}
(value, front)
}
pub fn deprecate_constant(module: Value, name: &CStr) {
unsafe { class::rb_deprecate_constant(module, name.as_ptr()) }
}