#[cfg(ruby_use_flonum)]
mod flonum;
use std::{
borrow::{Borrow, Cow},
cell::UnsafeCell,
ffi::CStr,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
mem::transmute,
num::NonZeroUsize,
ops::{Deref, DerefMut},
os::raw::{c_char, c_int, c_long, c_ulong},
ptr,
sync::Once,
};
#[cfg(ruby_use_flonum)]
pub use flonum::Flonum;
use rb_sys::{
rb_any_to_s, rb_block_call_kw, rb_check_funcall_kw, rb_check_id, rb_check_id_cstr,
rb_check_symbol_cstr, rb_enumeratorize_with_size_kw, rb_eql, rb_equal,
rb_funcall_with_block_kw, rb_funcallv_kw, rb_funcallv_public_kw, rb_gc_register_address,
rb_gc_unregister_address, rb_hash, rb_id2name, rb_id2sym, rb_inspect, rb_intern3, rb_ll2inum,
rb_obj_as_string, rb_obj_classname, rb_obj_freeze, rb_obj_is_kind_of, rb_obj_respond_to,
rb_sym2id, rb_ull2inum, ruby_fl_type, ruby_special_consts, ruby_value_type, RBasic, ID, VALUE,
};
const RUBY_FIXNUM_MAX: c_ulong = (c_long::MAX / 2) as c_ulong;
const RUBY_FIXNUM_MIN: c_long = c_long::MIN / 2;
use crate::{
block::Proc,
class::RClass,
encoding::EncodingCapable,
enumerator::Enumerator,
error::{protect, Error},
gc,
integer::{Integer, IntegerType},
into_value::{kw_splat, ArgList, IntoValue, IntoValueFromNative},
method::{Block, BlockReturn},
module::Module,
numeric::Numeric,
r_bignum::RBignum,
r_string::RString,
symbol::{IntoSymbol, Symbol},
try_convert::{TryConvert, TryConvertOwned},
Ruby,
};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Value(VALUE, PhantomData<*mut RBasic>);
impl Value {
#[inline]
pub(crate) const fn new(val: VALUE) -> Self {
Self(val, PhantomData)
}
#[inline]
pub(crate) const fn as_rb_value(self) -> VALUE {
self.0
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inspect())
}
}
impl IntoValue for Value {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self
}
}
impl IntoValue for i8 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_i64(self as i64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for i8 {}
impl IntoValue for i16 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_i64(self as i64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for i16 {}
impl IntoValue for i32 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_i64(self as i64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for i32 {}
impl IntoValue for i64 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_i64(self).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for i64 {}
impl IntoValue for i128 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_i128(self).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for i128 {}
impl IntoValue for isize {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_i64(self as i64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for isize {}
impl IntoValue for u8 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_u64(self as u64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for u8 {}
impl IntoValue for u16 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_u64(self as u64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for u16 {}
impl IntoValue for u32 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_u64(self as u64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for u32 {}
impl IntoValue for u64 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_u64(self).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for u64 {}
impl IntoValue for u128 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_u128(self).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for u128 {}
impl IntoValue for usize {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.integer_from_u64(self as u64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for usize {}
impl IntoValue for f32 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.float_from_f64(self as f64).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for f32 {}
impl IntoValue for f64 {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.float_from_f64(self).into_value_with(handle)
}
}
unsafe impl IntoValueFromNative for f64 {}
impl TryConvert for Value {
#[inline]
fn try_convert(val: Value) -> Result<Self, Error> {
Ok(val)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Opaque<T>(T);
#[doc(hidden)]
pub trait OpaqueVal {
type Val: ReprValue;
}
impl<T> OpaqueVal for Opaque<T>
where
T: ReprValue,
{
type Val = T;
}
impl<T> From<T> for Opaque<T>
where
T: ReprValue,
{
#[inline]
fn from(val: T) -> Self {
Self(val)
}
}
impl<T> IntoValue for Opaque<T>
where
T: IntoValue,
{
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
self.0.into_value_with(handle)
}
}
pub trait InnerValue {
type Value: ReprValue;
fn get_inner_with(self, ruby: &Ruby) -> Self::Value;
}
impl<T> InnerValue for Opaque<T>
where
T: ReprValue,
{
type Value = T;
#[inline]
fn get_inner_with(self, _: &Ruby) -> Self::Value {
self.0
}
}
pub trait InnerRef {
type Value: ReprValue;
fn get_inner_ref_with<'a>(&'a self, ruby: &Ruby) -> &'a Self::Value;
}
impl<T> InnerRef for Opaque<T>
where
T: ReprValue,
{
type Value = T;
#[inline]
fn get_inner_ref_with<'a>(&'a self, _: &Ruby) -> &'a Self::Value {
&self.0
}
}
impl Ruby {
#[inline]
pub fn get_inner<T>(&self, wrapper: impl InnerValue<Value = T>) -> T
where
T: ReprValue,
{
wrapper.get_inner_with(self)
}
#[inline]
pub fn get_inner_ref<'a, T>(&self, wrapper: &'a impl InnerRef<Value = T>) -> &'a T
where
T: ReprValue,
{
wrapper.get_inner_ref_with(self)
}
}
unsafe impl<T: ReprValue> Send for Opaque<T> {}
unsafe impl<T: ReprValue> Sync for Opaque<T> {}
pub struct Lazy<T: ReprValue> {
init: Once,
mark: bool,
func: fn(&Ruby) -> T,
value: UnsafeCell<Value>,
}
impl<T> Lazy<T>
where
T: ReprValue,
{
pub const fn new(func: fn(&Ruby) -> T) -> Self {
Self {
init: Once::new(),
mark: true,
func,
value: UnsafeCell::new(QNIL.0.get()),
}
}
pub const unsafe fn new_without_mark(func: fn(&Ruby) -> T) -> Self {
Self {
init: Once::new(),
mark: false,
func,
value: UnsafeCell::new(QNIL.0.get()),
}
}
#[inline]
pub fn force(this: &Self, handle: &Ruby) {
handle.get_inner(this);
}
pub fn try_get_inner(this: &Self) -> Option<Opaque<T>> {
unsafe {
this.init
.is_completed()
.then(|| T::from_value_unchecked(*this.value.get()).into())
}
}
}
unsafe impl<T: ReprValue> Sync for Lazy<T> {}
impl<T> InnerValue for &Lazy<T>
where
T: ReprValue,
{
type Value = T;
#[inline]
fn get_inner_with(self, ruby: &Ruby) -> Self::Value {
*self.get_inner_ref_with(ruby)
}
}
impl<T> InnerRef for Lazy<T>
where
T: ReprValue,
{
type Value = T;
fn get_inner_ref_with<'a>(&'a self, ruby: &Ruby) -> &'a Self::Value {
unsafe {
if !self.init.is_completed() {
let value = (self.func)(ruby);
self.init.call_once(|| {
if self.mark {
gc::register_mark_object(value);
}
*self.value.get() = value.as_value();
});
}
T::ref_from_ref_value_unchecked(&*self.value.get())
}
}
}
pub(crate) mod private {
use super::*;
use crate::value::ReprValue as _;
pub unsafe trait ReprValue: Copy {
#[inline]
unsafe fn from_value_unchecked(val: Value) -> Self {
*(&val as *const Value as *const Self)
}
#[inline]
unsafe fn ref_from_ref_value_unchecked(val: &Value) -> &Self {
&*(val as *const Value as *const Self)
}
#[inline]
fn copy_as_value(self) -> Value {
unsafe { *(&self as *const Self as *const Value) }
}
#[inline]
fn as_value_ref(&self) -> &Value {
unsafe { &*(self as *const Self as *const Value) }
}
#[inline]
fn as_rb_value(self) -> VALUE {
self.copy_as_value().0
}
#[inline]
unsafe fn r_basic_unchecked(self) -> ptr::NonNull<RBasic> {
#[cfg(debug_assertions)]
if self.is_immediate() {
panic!("attempting to access immediate value as pointer");
}
ptr::NonNull::new_unchecked(self.copy_as_value().0 as *mut RBasic)
}
#[inline]
fn is_immediate(self) -> bool {
let value_p = self.as_rb_value();
let immediate_p = value_p & ruby_special_consts::RUBY_IMMEDIATE_MASK as VALUE != 0;
let test = value_p & !(ruby_special_consts::RUBY_Qnil as VALUE) != 0;
immediate_p || !test }
#[inline]
fn r_basic(self) -> Option<ptr::NonNull<RBasic>> {
unsafe { (!self.is_immediate()).then(|| self.r_basic_unchecked()) }
}
#[inline]
fn is_false(self) -> bool {
self.as_rb_value() == ruby_special_consts::RUBY_Qfalse as VALUE
}
#[inline]
fn is_true(self) -> bool {
self.as_rb_value() == ruby_special_consts::RUBY_Qtrue as VALUE
}
#[inline]
fn is_undef(self) -> bool {
self.as_rb_value() == ruby_special_consts::RUBY_Qundef as VALUE
}
#[inline]
fn is_fixnum(self) -> bool {
self.as_rb_value() & ruby_special_consts::RUBY_FIXNUM_FLAG as VALUE != 0
}
#[inline]
fn is_static_symbol(self) -> bool {
const MASK: usize = !(usize::MAX << ruby_special_consts::RUBY_SPECIAL_SHIFT as usize);
self.as_rb_value() as usize & MASK == ruby_special_consts::RUBY_SYMBOL_FLAG as usize
}
#[inline]
fn is_flonum(self) -> bool {
self.as_rb_value() & ruby_special_consts::RUBY_FLONUM_MASK as VALUE
== ruby_special_consts::RUBY_FLONUM_FLAG as VALUE
}
#[inline]
fn rb_type(self) -> ruby_value_type {
match self.r_basic() {
Some(r_basic) => {
unsafe {
let ret = r_basic.as_ref().flags & (ruby_value_type::RUBY_T_MASK as VALUE);
std::mem::transmute(ret as u32)
}
}
None => {
if self.is_false() {
ruby_value_type::RUBY_T_FALSE
} else if self.copy_as_value().is_nil() {
ruby_value_type::RUBY_T_NIL
} else if self.is_true() {
ruby_value_type::RUBY_T_TRUE
} else if self.is_undef() {
ruby_value_type::RUBY_T_UNDEF
} else if self.is_fixnum() {
ruby_value_type::RUBY_T_FIXNUM
} else if self.is_static_symbol() {
ruby_value_type::RUBY_T_SYMBOL
} else if self.is_flonum() {
ruby_value_type::RUBY_T_FLOAT
} else {
unreachable!()
}
}
}
}
#[allow(clippy::wrong_self_convention)]
unsafe fn to_s_infallible(&self) -> Cow<'_, str> {
match self.as_value_ref().to_s() {
Ok(v) => v,
Err(_) => Cow::Owned(
RString::from_rb_value_unchecked(rb_any_to_s(self.as_rb_value()))
.to_string_lossy()
.into_owned(),
),
}
}
}
}
use private::ReprValue as _;
pub trait ReprValue: private::ReprValue {
#[inline]
fn as_value(self) -> Value {
unsafe { *(&self as *const Self as *const Value) }
}
#[inline]
fn is_nil(self) -> bool {
self.as_rb_value() == ruby_special_consts::RUBY_Qnil as VALUE
}
fn equal<T>(self, other: T) -> Result<bool, Error>
where
T: ReprValue,
{
unsafe {
protect(|| Value::new(rb_equal(self.as_rb_value(), other.as_rb_value())))
.map(Value::to_bool)
}
}
fn eql<T>(self, other: T) -> Result<bool, Error>
where
T: ReprValue,
{
unsafe {
protect(|| Value::new(rb_eql(self.as_rb_value(), other.as_rb_value()) as VALUE))
.map(Value::to_bool)
}
}
fn hash(self) -> Result<Integer, Error> {
unsafe { protect(|| Integer::from_rb_value_unchecked(rb_hash(self.as_rb_value()))) }
}
fn class(self) -> RClass {
let handle = Ruby::get_with(self);
unsafe {
match self.r_basic() {
Some(r_basic) => RClass::from_rb_value_unchecked(r_basic.as_ref().klass),
None => {
if self.is_false() {
handle.class_false_class()
} else if self.is_nil() {
handle.class_nil_class()
} else if self.is_true() {
handle.class_true_class()
} else if self.is_undef() {
panic!("undef does not have a class")
} else if self.is_fixnum() {
handle.class_integer()
} else if self.is_static_symbol() {
handle.class_symbol()
} else if self.is_flonum() {
handle.class_float()
} else {
unreachable!()
}
}
}
}
}
fn is_frozen(self) -> bool {
match self.r_basic() {
None => true,
Some(r_basic) => unsafe {
r_basic.as_ref().flags & ruby_fl_type::RUBY_FL_FREEZE as VALUE != 0
},
}
}
fn check_frozen(self) -> Result<(), Error> {
if self.is_frozen() {
Err(Error::new(
Ruby::get_with(self).exception_frozen_error(),
format!("can't modify frozen {}", unsafe { self.classname() }),
))
} else {
Ok(())
}
}
fn freeze(self) {
unsafe { rb_obj_freeze(self.as_rb_value()) };
}
#[inline]
fn to_bool(self) -> bool {
self.as_rb_value() & !(ruby_special_consts::RUBY_Qnil as VALUE) != 0
}
fn funcall<M, A, T>(self, method: M, args: A) -> Result<T, Error>
where
M: IntoId,
A: ArgList,
T: TryConvert,
{
let handle = Ruby::get_with(self);
let id = method.into_id_with(&handle);
let kw_splat = kw_splat(&args);
let args = args.into_arg_list_with(&handle);
let slice = args.as_ref();
unsafe {
protect(|| {
Value::new(rb_funcallv_kw(
self.as_rb_value(),
id.as_rb_id(),
slice.len() as c_int,
slice.as_ptr() as *const VALUE,
kw_splat as c_int,
))
})
.and_then(TryConvert::try_convert)
}
}
fn funcall_public<M, A, T>(self, method: M, args: A) -> Result<T, Error>
where
M: IntoId,
A: ArgList,
T: TryConvert,
{
let handle = Ruby::get_with(self);
let id = method.into_id_with(&handle);
let kw_splat = kw_splat(&args);
let args = args.into_arg_list_with(&handle);
let slice = args.as_ref();
unsafe {
protect(|| {
Value::new(rb_funcallv_public_kw(
self.as_rb_value(),
id.as_rb_id(),
slice.len() as c_int,
slice.as_ptr() as *const VALUE,
kw_splat as c_int,
))
})
.and_then(TryConvert::try_convert)
}
}
fn check_funcall<M, A, T>(self, method: M, args: A) -> Option<Result<T, Error>>
where
M: IntoId,
A: ArgList,
T: TryConvert,
{
let handle = Ruby::get_with(self);
let id = method.into_id_with(&handle);
let kw_splat = kw_splat(&args);
let args = args.into_arg_list_with(&handle);
let slice = args.as_ref();
unsafe {
let result = protect(|| {
Value::new(rb_check_funcall_kw(
self.as_rb_value(),
id.as_rb_id(),
slice.len() as c_int,
slice.as_ptr() as *const VALUE,
kw_splat as c_int,
))
});
match result {
Ok(v) if v.is_undef() => None,
Ok(v) => Some(T::try_convert(v)),
Err(e) => Some(Err(e)),
}
}
}
fn funcall_with_block<M, A, T>(self, method: M, args: A, block: Proc) -> Result<T, Error>
where
M: IntoId,
A: ArgList,
T: TryConvert,
{
let handle = Ruby::get_with(self);
let id = method.into_id_with(&handle);
let kw_splat = kw_splat(&args);
let args = args.into_arg_list_with(&handle);
let slice = args.as_ref();
unsafe {
protect(|| {
Value::new(rb_funcall_with_block_kw(
self.as_rb_value(),
id.as_rb_id(),
slice.len() as c_int,
slice.as_ptr() as *const VALUE,
block.as_rb_value(),
kw_splat as c_int,
))
})
.and_then(TryConvert::try_convert)
}
}
fn block_call<M, A, R, T>(
self,
method: M,
args: A,
block: fn(&Ruby, &[Value], Option<Proc>) -> R,
) -> Result<T, Error>
where
M: IntoId,
A: ArgList,
R: BlockReturn,
T: TryConvert,
{
unsafe extern "C" fn call<R>(
_yielded_arg: VALUE,
callback_arg: VALUE,
argc: c_int,
argv: *const VALUE,
blockarg: VALUE,
) -> VALUE
where
R: BlockReturn,
{
let func =
std::mem::transmute::<VALUE, fn(&Ruby, &[Value], Option<Proc>) -> R>(callback_arg);
func.call_handle_error(argc, argv as *const Value, Value::new(blockarg))
.as_rb_value()
}
let handle = Ruby::get_with(self);
let id = method.into_id_with(&handle);
let kw_splat = kw_splat(&args);
let args = args.into_arg_list_with(&handle);
let slice = args.as_ref();
let call_func =
call::<R> as unsafe extern "C" fn(VALUE, VALUE, c_int, *const VALUE, VALUE) -> VALUE;
protect(|| unsafe {
#[allow(clippy::fn_to_numeric_cast)]
Value::new(rb_block_call_kw(
self.as_rb_value(),
id.as_rb_id(),
slice.len() as c_int,
slice.as_ptr() as *const VALUE,
Some(call_func),
block as VALUE,
kw_splat as c_int,
))
})
.and_then(TryConvert::try_convert)
}
fn respond_to<M>(self, method: M, include_private: bool) -> Result<bool, Error>
where
M: IntoId,
{
let handle = Ruby::get_with(self);
let id = method.into_id_with(&handle);
let mut res = false;
protect(|| {
unsafe {
res = rb_obj_respond_to(self.as_rb_value(), id.as_rb_id(), include_private as c_int)
!= 0
};
handle.qnil()
})?;
Ok(res)
}
fn to_r_string(self) -> Result<RString, Error> {
match RString::from_value(self.as_value()) {
Some(v) => Ok(v),
None => protect(|| unsafe {
RString::from_rb_value_unchecked(rb_obj_as_string(self.as_rb_value()))
}),
}
}
#[allow(clippy::wrong_self_convention)]
unsafe fn to_s(&self) -> Result<Cow<'_, str>, Error> {
if let Some(s) = RString::ref_from_value(self.as_value_ref()) {
if s.is_utf8_compatible_encoding() {
return s.as_str().map(Cow::Borrowed);
} else {
return (*s).to_string().map(Cow::Owned);
}
}
self.to_r_string()
.and_then(|s| s.to_string().map(Cow::Owned))
}
fn inspect(self) -> String {
let handle = Ruby::get_with(self);
unsafe {
let s = protect(|| RString::from_rb_value_unchecked(rb_inspect(self.as_rb_value())))
.unwrap_or_else(|_| {
RString::from_rb_value_unchecked(rb_any_to_s(self.as_rb_value()))
});
s.conv_enc(handle.utf8_encoding())
.unwrap_or(s)
.to_string_lossy()
.into_owned()
}
}
unsafe fn classname(&self) -> Cow<'_, str> {
let ptr = rb_obj_classname(self.as_rb_value());
let cstr = CStr::from_ptr(ptr);
cstr.to_string_lossy()
}
fn is_kind_of<T>(self, class: T) -> bool
where
T: ReprValue + Module,
{
unsafe { Value::new(rb_obj_is_kind_of(self.as_rb_value(), class.as_rb_value())).to_bool() }
}
fn enumeratorize<M, A>(self, method: M, args: A) -> Enumerator
where
M: IntoSymbol,
A: ArgList,
{
let handle = Ruby::get_with(self);
let kw_splat = kw_splat(&args);
let args = args.into_arg_list_with(&handle);
let slice = args.as_ref();
unsafe {
Enumerator::from_rb_value_unchecked(rb_enumeratorize_with_size_kw(
self.as_rb_value(),
method.into_symbol_with(&handle).as_rb_value(),
slice.len() as c_int,
slice.as_ptr() as *const VALUE,
None,
kw_splat as c_int,
))
}
}
}
unsafe impl private::ReprValue for Value {}
impl ReprValue for Value {}
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub(crate) struct NonZeroValue(NonZeroUsize, PhantomData<ptr::NonNull<RBasic>>);
impl NonZeroValue {
#[inline]
pub(crate) const unsafe fn new_unchecked(val: Value) -> Self {
Self(
NonZeroUsize::new_unchecked(val.as_rb_value() as usize),
PhantomData,
)
}
#[inline]
pub(crate) const fn get(self) -> Value {
Value::new(self.0.get() as VALUE)
}
}
pub struct BoxValue<T>(Box<T>);
impl<T> BoxValue<T>
where
T: ReprValue,
{
pub fn new(val: T) -> Self {
let mut boxed = Box::new(val);
unsafe { rb_gc_register_address(boxed.as_mut() as *mut _ as *mut VALUE) };
Self(boxed)
}
}
impl<T> Drop for BoxValue<T> {
fn drop(&mut self) {
unsafe {
rb_gc_unregister_address(self.0.as_mut() as *mut _ as *mut VALUE);
}
}
}
impl<T> AsRef<T> for BoxValue<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T> AsMut<T> for BoxValue<T> {
#[inline]
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> Deref for BoxValue<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for BoxValue<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> fmt::Display for BoxValue<T>
where
T: ReprValue,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.as_value().to_s_infallible() })
}
}
impl<T> fmt::Debug for BoxValue<T>
where
T: ReprValue,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_value().inspect())
}
}
impl<T> IntoValue for BoxValue<T>
where
T: ReprValue,
{
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.as_value()
}
}
unsafe impl<T> IntoValueFromNative for BoxValue<T> where T: ReprValue {}
impl Ruby {
#[inline]
pub fn qfalse(&self) -> Qfalse {
QFALSE
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qfalse(Value);
const QFALSE: Qfalse = Qfalse::new();
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::qfalse` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn qfalse() -> Qfalse {
get_ruby!().qfalse()
}
impl Qfalse {
#[inline]
const fn new() -> Self {
Qfalse(Value::new(ruby_special_consts::RUBY_Qfalse as VALUE))
}
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
val.is_false().then(Self::new)
}
}
impl fmt::Display for Qfalse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for Qfalse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inspect())
}
}
impl IntoValue for Qfalse {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.0
}
}
unsafe impl private::ReprValue for Qfalse {}
impl ReprValue for Qfalse {}
impl TryConvert for Qfalse {
fn try_convert(val: Value) -> Result<Self, Error> {
Self::from_value(val).ok_or_else(|| {
Error::new(
Ruby::get_with(val).exception_type_error(),
format!("no implicit conversion of {} into FalseClass", unsafe {
val.classname()
},),
)
})
}
}
unsafe impl TryConvertOwned for Qfalse {}
impl Ruby {
#[inline]
pub fn qnil(&self) -> Qnil {
QNIL
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qnil(NonZeroValue);
const QNIL: Qnil = Qnil::new();
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::qnil` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn qnil() -> Qnil {
get_ruby!().qnil()
}
impl Qnil {
#[inline]
const fn new() -> Self {
unsafe {
Self(NonZeroValue::new_unchecked(Value::new(
ruby_special_consts::RUBY_Qnil as VALUE,
)))
}
}
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
val.is_nil().then(Self::new)
}
}
impl fmt::Display for Qnil {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for Qnil {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inspect())
}
}
impl IntoValue for Qnil {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.0.get()
}
}
impl IntoValue for () {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
handle.qnil().as_value()
}
}
unsafe impl IntoValueFromNative for () {}
impl<T> IntoValue for Option<T>
where
T: IntoValue,
{
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
match self {
Some(t) => handle.into_value(t),
None => handle.qnil().as_value(),
}
}
}
unsafe impl<T> IntoValueFromNative for Option<T> where T: IntoValueFromNative {}
unsafe impl private::ReprValue for Qnil {}
impl ReprValue for Qnil {}
impl TryConvert for Qnil {
fn try_convert(val: Value) -> Result<Self, Error> {
Self::from_value(val).ok_or_else(|| {
Error::new(
Ruby::get_with(val).exception_type_error(),
format!("no implicit conversion of {} into NilClass", unsafe {
val.classname()
},),
)
})
}
}
unsafe impl TryConvertOwned for Qnil {}
impl Ruby {
#[inline]
pub fn qtrue(&self) -> Qtrue {
QTRUE
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qtrue(NonZeroValue);
const QTRUE: Qtrue = Qtrue::new();
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::qtrue` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn qtrue() -> Qtrue {
get_ruby!().qtrue()
}
impl Qtrue {
#[inline]
const fn new() -> Self {
unsafe {
Self(NonZeroValue::new_unchecked(Value::new(
ruby_special_consts::RUBY_Qtrue as VALUE,
)))
}
}
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
val.is_true().then(Self::new)
}
}
impl fmt::Display for Qtrue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for Qtrue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inspect())
}
}
impl IntoValue for Qtrue {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.0.get()
}
}
impl IntoValue for bool {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
if self {
handle.qtrue().as_value()
} else {
handle.qfalse().as_value()
}
}
}
unsafe impl IntoValueFromNative for bool {}
unsafe impl private::ReprValue for Qtrue {}
impl ReprValue for Qtrue {}
impl TryConvert for Qtrue {
fn try_convert(val: Value) -> Result<Self, Error> {
Self::from_value(val).ok_or_else(|| {
Error::new(
Ruby::get_with(val).exception_type_error(),
format!("no implicit conversion of {} into TrueClass", unsafe {
val.classname()
},),
)
})
}
}
unsafe impl TryConvertOwned for Qtrue {}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qundef(NonZeroValue);
pub const QUNDEF: Qundef = Qundef::new();
impl Qundef {
#[inline]
const fn new() -> Self {
unsafe {
Self(NonZeroValue::new_unchecked(Value::new(
ruby_special_consts::RUBY_Qundef as VALUE,
)))
}
}
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
val.is_undef().then(Self::new)
}
#[inline]
pub unsafe fn as_value(self) -> Value {
self.0.get()
}
}
impl Ruby {
#[inline]
pub fn fixnum_from_i64(&self, n: i64) -> Result<Fixnum, RBignum> {
Fixnum::from_i64_impl(n)
.ok_or_else(|| unsafe { RBignum::from_rb_value_unchecked(rb_ll2inum(n)) })
}
#[inline]
pub fn fixnum_from_u64(&self, n: u64) -> Result<Fixnum, RBignum> {
Fixnum::from_i64_impl(i64::try_from(n).unwrap_or(i64::MAX))
.ok_or_else(|| unsafe { RBignum::from_rb_value_unchecked(rb_ull2inum(n)) })
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Fixnum(NonZeroValue);
impl Fixnum {
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
unsafe {
val.is_fixnum()
.then(|| Self(NonZeroValue::new_unchecked(val)))
}
}
#[inline]
pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
Self(NonZeroValue::new_unchecked(Value::new(val)))
}
#[inline]
pub(crate) fn from_i64_impl(n: i64) -> Option<Self> {
#[allow(clippy::useless_conversion)] (c_ulong::try_from(n)
.map(|n| n < RUBY_FIXNUM_MAX + 1)
.unwrap_or(false)
&& c_long::try_from(n)
.map(|n| n >= RUBY_FIXNUM_MIN)
.unwrap_or(false))
.then(|| unsafe {
let x = transmute::<_, usize>(n as isize);
Self::from_rb_value_unchecked(x.wrapping_add(x.wrapping_add(1)) as VALUE)
})
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::fixnum_from_i64` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn from_i64(n: i64) -> Result<Self, RBignum> {
get_ruby!().fixnum_from_i64(n)
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::fixnum_from_u64` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn from_u64(n: u64) -> Result<Self, RBignum> {
get_ruby!().fixnum_from_u64(n)
}
fn is_negative(self) -> bool {
unsafe { transmute::<_, isize>(self.0) < 0 }
}
#[inline]
pub fn to_i8(self) -> Result<i8, Error> {
let res = self.to_isize();
if res > i8::MAX as isize || res < i8::MIN as isize {
return Err(Error::new(
Ruby::get_with(self).exception_range_error(),
"fixnum too big to convert into `i8`",
));
}
Ok(res as i8)
}
#[inline]
pub fn to_i16(self) -> Result<i16, Error> {
let res = self.to_isize();
if res > i16::MAX as isize || res < i16::MIN as isize {
return Err(Error::new(
Ruby::get_with(self).exception_range_error(),
"fixnum too big to convert into `i16`",
));
}
Ok(res as i16)
}
#[inline]
pub fn to_i32(self) -> Result<i32, Error> {
let res = self.to_isize();
if res > i32::MAX as isize || res < i32::MIN as isize {
return Err(Error::new(
Ruby::get_with(self).exception_range_error(),
"fixnum too big to convert into `i32`",
));
}
Ok(res as i32)
}
#[inline]
pub fn to_i64(self) -> i64 {
self.to_isize() as i64
}
#[inline]
pub fn to_i128(self) -> i128 {
self.to_isize() as i128
}
#[inline]
pub fn to_isize(self) -> isize {
unsafe { transmute::<_, isize>(self) >> 1 }
}
#[inline]
pub fn to_u8(self) -> Result<u8, Error> {
let handle = Ruby::get_with(self);
if self.is_negative() {
return Err(Error::new(
handle.exception_range_error(),
"can't convert negative integer to unsigned",
));
}
let res = self.to_isize();
if res > u8::MAX as isize {
return Err(Error::new(
handle.exception_range_error(),
"fixnum too big to convert into `u8`",
));
}
Ok(res as u8)
}
#[inline]
pub fn to_u16(self) -> Result<u16, Error> {
let handle = Ruby::get_with(self);
if self.is_negative() {
return Err(Error::new(
handle.exception_range_error(),
"can't convert negative integer to unsigned",
));
}
let res = self.to_isize();
if res > u16::MAX as isize {
return Err(Error::new(
handle.exception_range_error(),
"fixnum too big to convert into `u16`",
));
}
Ok(res as u16)
}
#[inline]
pub fn to_u32(self) -> Result<u32, Error> {
let handle = Ruby::get_with(self);
if self.is_negative() {
return Err(Error::new(
handle.exception_range_error(),
"can't convert negative integer to unsigned",
));
}
let res = self.to_isize();
if res > u32::MAX as isize {
return Err(Error::new(
handle.exception_range_error(),
"fixnum too big to convert into `u32`",
));
}
Ok(res as u32)
}
#[inline]
pub fn to_u64(self) -> Result<u64, Error> {
if self.is_negative() {
return Err(Error::new(
Ruby::get_with(self).exception_range_error(),
"can't convert negative integer to unsigned",
));
}
Ok(self.to_isize() as u64)
}
#[inline]
pub fn to_u128(self) -> Result<u128, Error> {
if self.is_negative() {
return Err(Error::new(
Ruby::get_with(self).exception_range_error(),
"can't convert negative integer to unsigned",
));
}
Ok(self.to_isize() as u128)
}
#[inline]
pub fn to_usize(self) -> Result<usize, Error> {
if self.is_negative() {
return Err(Error::new(
Ruby::get_with(self).exception_range_error(),
"can't convert negative integer to unsigned",
));
}
Ok(self.to_isize() as usize)
}
}
impl fmt::Display for Fixnum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for Fixnum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inspect())
}
}
impl IntoValue for Fixnum {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.0.get()
}
}
unsafe impl private::ReprValue for Fixnum {}
impl Numeric for Fixnum {}
impl ReprValue for Fixnum {}
impl TryConvert for Fixnum {
fn try_convert(val: Value) -> Result<Self, Error> {
match Integer::try_convert(val)?.integer_type() {
IntegerType::Fixnum(fix) => Ok(fix),
IntegerType::Bignum(_) => Err(Error::new(
Ruby::get_with(val).exception_range_error(),
"integer too big for fixnum",
)),
}
}
}
unsafe impl TryConvertOwned for Fixnum {}
impl Ruby {
#[inline]
pub fn sym_new<T>(&self, name: T) -> StaticSymbol
where
T: IntoId,
{
name.into_id_with(self).into()
}
pub fn check_symbol(&self, name: &str) -> Option<StaticSymbol> {
unsafe {
let res = Value::new(rb_check_symbol_cstr(
name.as_ptr() as *mut c_char,
name.len() as c_long,
self.utf8_encoding().as_ptr(),
));
(!res.is_nil()).then(|| StaticSymbol::from_rb_value_unchecked(res.as_rb_value()))
}
}
}
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct StaticSymbol(NonZeroValue);
impl StaticSymbol {
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
fn is_static_or_permanent_symbol(val: Value) -> bool {
if val.is_static_symbol() {
return true;
}
debug_assert_value!(val);
if val.rb_type() != ruby_value_type::RUBY_T_SYMBOL {
return false;
}
let mut p = val.as_rb_value();
unsafe { rb_check_id(&mut p as *mut _) != 0 }
}
unsafe {
is_static_or_permanent_symbol(val).then(|| Self(NonZeroValue::new_unchecked(val)))
}
}
#[inline]
pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
Self(NonZeroValue::new_unchecked(Value::new(val)))
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::sym_new` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn new<T>(name: T) -> Self
where
T: IntoId,
{
get_ruby!().sym_new(name)
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::check_symbol` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn check(name: &str) -> Option<Self> {
get_ruby!().check_symbol(name)
}
#[inline]
pub fn name(self) -> Result<&'static str, Error> {
Id::from(self).name()
}
}
impl Borrow<Symbol> for StaticSymbol {
fn borrow(&self) -> &Symbol {
unsafe { &*(self as *const Self as *const Symbol) }
}
}
impl fmt::Display for StaticSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for StaticSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inspect())
}
}
impl EncodingCapable for StaticSymbol {}
impl From<Id> for StaticSymbol {
fn from(id: Id) -> Self {
unsafe { Self::from_rb_value_unchecked(rb_id2sym(id.as_rb_id())) }
}
}
impl IntoValue for StaticSymbol {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.0.get()
}
}
impl PartialEq<Id> for StaticSymbol {
#[inline]
fn eq(&self, other: &Id) -> bool {
self.into_id_with(&Ruby::get_with(*self)) == *other
}
}
impl PartialEq<OpaqueId> for StaticSymbol {
#[inline]
fn eq(&self, other: &OpaqueId) -> bool {
self.into_id_with(&Ruby::get_with(*self)).0 == other.0
}
}
impl PartialEq<LazyId> for StaticSymbol {
#[inline]
fn eq(&self, other: &LazyId) -> bool {
self.into_id_with(&Ruby::get_with(*self)).0 == other.0
}
}
impl PartialEq<Symbol> for StaticSymbol {
#[inline]
fn eq(&self, other: &Symbol) -> bool {
other.as_static().map(|o| *self == o).unwrap_or(false)
}
}
unsafe impl private::ReprValue for StaticSymbol {}
impl ReprValue for StaticSymbol {}
impl TryConvert for StaticSymbol {
fn try_convert(val: Value) -> Result<Self, Error> {
Symbol::try_convert(val).map(|s| s.to_static())
}
}
unsafe impl TryConvertOwned for StaticSymbol {}
impl Ruby {
pub fn intern(&self, name: &str) -> Id {
Id::from_rb_id(unsafe {
rb_intern3(
name.as_ptr() as *const c_char,
name.len() as c_long,
self.utf8_encoding().as_ptr(),
)
})
}
pub fn check_id(&self, name: &str) -> Option<Id> {
let res = unsafe {
rb_check_id_cstr(
name.as_ptr() as *mut c_char,
name.len() as c_long,
self.utf8_encoding().as_ptr(),
)
};
(res != 0).then(|| Id::from_rb_id(res))
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Id(ID, PhantomData<*mut u8>);
impl Id {
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::intern` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
pub fn new<T>(name: T) -> Self
where
T: AsRef<str>,
{
get_ruby!().intern(name.as_ref())
}
#[inline]
pub(crate) fn from_rb_id(id: ID) -> Self {
Self(id, PhantomData)
}
#[inline]
pub(crate) fn as_rb_id(self) -> ID {
self.0
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::check_id` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn check(name: &str) -> Option<Self> {
get_ruby!().check_id(name)
}
pub fn name(self) -> Result<&'static str, Error> {
unsafe {
let ptr = rb_id2name(self.as_rb_id());
let cstr = CStr::from_ptr(ptr);
cstr.to_str().map_err(|e| {
Error::new(
Ruby::get_unchecked().exception_encoding_error(),
e.to_string(),
)
})
}
}
}
impl Borrow<OpaqueId> for Id {
fn borrow(&self) -> &OpaqueId {
unsafe { &*(self as *const Self as *const OpaqueId) }
}
}
pub trait IntoId: Sized {
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `IntoId::into_id_with` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
fn into_id(self) -> Id {
self.into_id_with(&get_ruby!())
}
#[inline]
unsafe fn into_id_unchecked(self) -> Id {
self.into_id_with(&Ruby::get_unchecked())
}
fn into_id_with(self, handle: &Ruby) -> Id;
}
impl IntoId for Id {
#[inline]
fn into_id_with(self, _: &Ruby) -> Id {
self
}
}
impl IntoId for &str {
#[inline]
fn into_id_with(self, handle: &Ruby) -> Id {
handle.intern(self)
}
}
impl IntoId for String {
#[inline]
fn into_id_with(self, handle: &Ruby) -> Id {
self.as_str().into_id_with(handle)
}
}
impl IntoId for StaticSymbol {
#[inline]
fn into_id_with(self, handle: &Ruby) -> Id {
self.into_symbol_with(handle).into_id_with(handle)
}
}
impl From<StaticSymbol> for Id {
#[inline]
fn from(sym: StaticSymbol) -> Self {
sym.into_id_with(&Ruby::get_with(sym))
}
}
impl IntoId for Symbol {
#[inline]
fn into_id_with(self, _: &Ruby) -> Id {
if self.is_static_symbol() {
Id::from_rb_id(self.as_rb_value() >> ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE)
} else {
Id::from_rb_id(unsafe { rb_sym2id(self.as_rb_value()) })
}
}
}
impl IntoValue for Id {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
StaticSymbol::from(self).into_value_with(handle)
}
}
impl From<Symbol> for Id {
#[inline]
fn from(sym: Symbol) -> Self {
sym.into_id_with(&Ruby::get_with(sym))
}
}
impl PartialEq<OpaqueId> for Id {
#[inline]
fn eq(&self, other: &OpaqueId) -> bool {
self.0 == other.0
}
}
impl PartialEq<LazyId> for Id {
#[inline]
fn eq(&self, other: &LazyId) -> bool {
self.0 == other.0
}
}
impl PartialEq<StaticSymbol> for Id {
#[inline]
fn eq(&self, other: &StaticSymbol) -> bool {
*self == other.into_id_with(&Ruby::get_with(*other))
}
}
impl PartialEq<Symbol> for Id {
#[inline]
fn eq(&self, other: &Symbol) -> bool {
other.as_static().map(|o| *self == o).unwrap_or(false)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct OpaqueId(ID);
impl From<Id> for OpaqueId {
#[inline]
fn from(id: Id) -> Self {
Self(id.0)
}
}
impl From<StaticSymbol> for OpaqueId {
#[inline]
fn from(sym: StaticSymbol) -> Self {
sym.into_id_with(&Ruby::get_with(sym)).into()
}
}
impl From<Symbol> for OpaqueId {
#[inline]
fn from(sym: Symbol) -> Self {
sym.into_id_with(&Ruby::get_with(sym)).into()
}
}
impl IntoId for OpaqueId {
#[inline]
fn into_id_with(self, _: &Ruby) -> Id {
Id::from_rb_id(self.0)
}
}
impl IntoSymbol for OpaqueId {
#[inline]
fn into_symbol_with(self, handle: &Ruby) -> Symbol {
self.into_id_with(handle).into_symbol_with(handle)
}
}
impl IntoValue for OpaqueId {
#[inline]
fn into_value_with(self, handle: &Ruby) -> Value {
self.into_symbol_with(handle).into_value_with(handle)
}
}
impl PartialEq<Id> for OpaqueId {
#[inline]
fn eq(&self, other: &Id) -> bool {
self.0 == other.0
}
}
impl PartialEq<LazyId> for OpaqueId {
#[inline]
fn eq(&self, other: &LazyId) -> bool {
*self == **other
}
}
impl PartialEq<StaticSymbol> for OpaqueId {
#[inline]
fn eq(&self, other: &StaticSymbol) -> bool {
*self == other.into_id_with(&Ruby::get_with(*other))
}
}
impl PartialEq<Symbol> for OpaqueId {
#[inline]
fn eq(&self, other: &Symbol) -> bool {
other.as_static().map(|o| *self == o).unwrap_or(false)
}
}
pub struct LazyId {
init: Once,
inner: UnsafeCell<LazyIdInner>,
}
union LazyIdInner {
name: &'static str,
value: OpaqueId,
}
impl LazyId {
pub const fn new(name: &'static str) -> Self {
Self {
init: Once::new(),
inner: UnsafeCell::new(LazyIdInner { name }),
}
}
#[inline]
pub fn force(this: &Self, handle: &Ruby) {
Self::get_inner_with(this, handle);
}
#[inline]
pub fn get_inner_with(this: &Self, handle: &Ruby) -> Id {
unsafe {
this.init.call_once(|| {
let inner = this.inner.get();
(*inner).value = handle.intern((*inner).name).into();
});
(*this.inner.get()).value.into_id_with(handle)
}
}
pub fn try_get_inner(this: &Self) -> Option<OpaqueId> {
unsafe { this.init.is_completed().then(|| (*this.inner.get()).value) }
}
}
unsafe impl Send for LazyId {}
unsafe impl Sync for LazyId {}
impl fmt::Debug for LazyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(non_camel_case_types)]
#[derive(Debug)]
struct uninit();
f.debug_tuple("LazyId")
.field(
Self::try_get_inner(self)
.as_ref()
.map(|v| v as &dyn fmt::Debug)
.unwrap_or(&uninit()),
)
.finish()
}
}
impl Deref for LazyId {
type Target = OpaqueId;
fn deref(&self) -> &Self::Target {
unsafe {
self.init.call_once(|| {
let inner = self.inner.get();
(*inner).value = Ruby::get().unwrap().intern((*inner).name).into();
});
&(*self.inner.get()).value
}
}
}
impl Hash for LazyId {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
impl PartialEq for LazyId {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.deref() == other.deref()
}
}
impl Eq for LazyId {}
impl PartialEq<Id> for LazyId {
#[inline]
fn eq(&self, other: &Id) -> bool {
*self.deref() == *other
}
}
impl PartialEq<StaticSymbol> for LazyId {
#[inline]
fn eq(&self, other: &StaticSymbol) -> bool {
*self == other.into_id_with(&Ruby::get_with(*other))
}
}
impl PartialEq<Symbol> for LazyId {
#[inline]
fn eq(&self, other: &Symbol) -> bool {
other.as_static().map(|o| *self == o).unwrap_or(false)
}
}