Skip to main content

magnus/
typed_data.rs

1//! Types and Traits for wrapping Rust types as Ruby objects.
2//!
3//! This, along with [`RTypedData`], provides a Rust API to the
4//! `rb_data_typed_object_wrap` function from Ruby's C API.
5
6use std::{
7    collections::hash_map::DefaultHasher,
8    ffi::{CStr, c_void},
9    fmt,
10    hash::Hasher,
11    marker::PhantomData,
12    mem::size_of_val,
13    ops::Deref,
14    panic::catch_unwind,
15    ptr,
16};
17
18use rb_sys::{
19    self, RTYPEDDATA_GET_DATA, VALUE, rb_data_type_struct__bindgen_ty_1, rb_data_type_t,
20    rb_gc_writebarrier, rb_gc_writebarrier_unprotect, rb_obj_reveal, rb_singleton_class_attached,
21    rb_singleton_class_clone,
22    rbimpl_typeddata_flags::{self, RUBY_TYPED_FREE_IMMEDIATELY, RUBY_TYPED_WB_PROTECTED},
23    size_t,
24};
25
26use crate::{
27    Ruby,
28    class::RClass,
29    error::{Error, bug_from_panic},
30    gc::{self, Mark},
31    into_value::IntoValue,
32    object::Object,
33    r_typed_data::RTypedData,
34    scan_args::{get_kwargs, scan_args},
35    try_convert::TryConvert,
36    value::{
37        ReprValue, Value,
38        private::{self, ReprValue as _},
39    },
40};
41
42/// A C struct containing metadata on a Rust type, for use with the
43/// `rb_data_typed_object_wrap` API.
44#[repr(transparent)]
45pub struct DataType(rb_data_type_t);
46
47impl DataType {
48    /// Create a new `DataTypeBuilder`.
49    ///
50    /// `name` should be unique per wrapped type. It does not need to be a
51    /// valid Ruby identifier.
52    ///
53    /// See [`data_type_builder`](macro@crate::data_type_builder) to create a
54    /// `DataTypeBuilder` with a `'static CStr` `name` from a string literal.
55    /// # Examples
56    ///
57    /// ```
58    /// use magnus::DataType;
59    /// # use magnus::DataTypeFunctions;
60    /// # #[derive(DataTypeFunctions)]
61    /// # struct Example();
62    ///
63    /// DataType::builder::<Example>(c"example");
64    /// ```
65    pub const fn builder<T>(name: &'static CStr) -> DataTypeBuilder<T>
66    where
67        T: DataTypeFunctions,
68    {
69        DataTypeBuilder::new(name)
70    }
71
72    #[inline]
73    pub(crate) fn as_rb_data_type(&self) -> &rb_data_type_t {
74        &self.0
75    }
76}
77
78unsafe impl Send for DataType {}
79unsafe impl Sync for DataType {}
80
81/// A helper trait used to define functions associated with a [`DataType`].
82pub trait DataTypeFunctions
83where
84    Self: Send + Sized,
85{
86    /// Called when the Ruby wrapper object is garbage collected.
87    ///
88    /// This can be implemented to perform Ruby-specific clean up when your
89    /// type is no longer referenced from Ruby, but it is likely easier to do
90    /// this in a [`Drop`] implementation for your type.
91    ///
92    /// This function will always be called by Ruby on GC, it can not be opted
93    /// out of.
94    ///
95    /// The default implementation simply drops `self`.
96    ///
97    /// If this function (or the [`Drop`] implementation for your type) call
98    /// Ruby APIs you should not enable the `free_immediately` flag with the
99    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
100    /// macro or [`DataTypeBuilder::free_immediately`].
101    ///
102    /// This function **must not** panic. The process will abort if this
103    /// function panics.
104    fn free(self: Box<Self>) {}
105
106    /// Called when Ruby marks this object as part of garbage collection.
107    ///
108    /// If your type contains any Ruby values you must mark each of those
109    /// values in this function to avoid them being garbage collected.
110    ///
111    /// This function is only called when the `mark` flag is set with the
112    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
113    /// macro or [`DataTypeBuilder::mark`].
114    ///
115    /// The default implementation does nothing.
116    ///
117    /// This function **must not** panic. The process will abort if this
118    /// function panics.
119    fn mark(&self, #[allow(unused_variables)] marker: &gc::Marker) {}
120
121    /// Called by Ruby to establish the memory size of this data, to optimise
122    /// when garbage collection happens.
123    ///
124    /// This function is only called when the `size` flag is set with the
125    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
126    /// macro or [`DataTypeBuilder::mark`].
127    ///
128    /// The default implementation delegates to [`std::mem::size_of_val`].
129    ///
130    /// This function **must not** panic. The process will abort if this
131    /// function panics.
132    fn size(&self) -> usize {
133        size_of_val(self)
134    }
135
136    /// Called during garbage collection.
137    ///
138    /// If your type contains any Ruby values that you have marked as moveable
139    /// in your [`mark`](Self::mark) function, you must update them in this
140    /// function using [`gc::Compactor::location`].
141    ///
142    /// Ruby considers values are moveable if marked with the
143    /// [`gc::Marker::mark_movable`] function. Other marking functions such as
144    /// [`gc::Marker::mark`] will prevent values being moved.
145    ///
146    /// As it is only safe for this function to receive a shared `&self`
147    /// reference, you must implement interior mutability to be able to update
148    /// values. This is very hard to do correctly, and it is recommended to
149    /// simply avoid using [`gc::Marker::mark_movable`] and `compact`.
150    ///
151    /// This function is only called when the `compact` flag is set with the
152    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
153    /// macro or [`DataTypeBuilder::mark`].
154    ///
155    /// The default implementation does nothing.
156    ///
157    /// This function **must not** panic. The process will abort if this
158    /// function panics.
159    fn compact(&self, #[allow(unused_variables)] compactor: &gc::Compactor) {}
160
161    /// Extern wrapper for `free`. Don't define or call.
162    ///
163    /// # Safety
164    ///
165    /// `ptr` must be a valid pointer to a `Box<Self>`, and must not be aliased
166    /// This function will free the memory pointed to by `ptr`.
167    ///
168    /// This function must not panic.
169    #[doc(hidden)]
170    unsafe extern "C" fn extern_free(ptr: *mut c_void) {
171        unsafe {
172            if let Err(e) = catch_unwind(|| Self::free(Box::from_raw(ptr as *mut _))) {
173                bug_from_panic(e, "panic in DataTypeFunctions::free")
174            }
175        }
176    }
177
178    /// Extern wrapper for `mark`. Don't define or call.
179    ///
180    /// # Safety
181    ///
182    /// `ptr` must be a valid pointer to a `Self`, and must not be aliased.
183    ///
184    /// This function must not panic.
185    #[doc(hidden)]
186    unsafe extern "C" fn extern_mark(ptr: *mut c_void) {
187        unsafe {
188            let marker = gc::Marker::new(Ruby::get_unchecked());
189            if let Err(e) = catch_unwind(|| Self::mark(&*(ptr as *mut Self), &marker)) {
190                bug_from_panic(e, "panic in DataTypeFunctions::mark")
191            }
192        }
193    }
194
195    /// Extern wrapper for `size`. Don't define or call.
196    ///
197    /// # Safety
198    ///
199    /// `ptr` must be a valid pointer to a `Self`.
200    ///
201    /// This function must not panic.
202    #[doc(hidden)]
203    unsafe extern "C" fn extern_size(ptr: *const c_void) -> size_t {
204        unsafe {
205            match catch_unwind(|| Self::size(&*(ptr as *const Self)) as size_t) {
206                Ok(v) => v,
207                Err(e) => bug_from_panic(e, "panic in DataTypeFunctions::size"),
208            }
209        }
210    }
211
212    /// Extern wrapper for `compact`. Don't define or call.
213    ///
214    /// # Safety
215    ///
216    /// `ptr` must be a valid pointer to a `Self`, and must not be aliased.
217    ///
218    /// This function must not panic.
219    #[doc(hidden)]
220    unsafe extern "C" fn extern_compact(ptr: *mut c_void) {
221        unsafe {
222            let compactor = gc::Compactor::new(Ruby::get_unchecked());
223            if let Err(e) = catch_unwind(|| Self::compact(&*(ptr as *mut Self), &compactor)) {
224                bug_from_panic(e, "panic in DataTypeFunctions::compact")
225            }
226        }
227    }
228}
229
230/// A builder for [`DataType`].
231pub struct DataTypeBuilder<T> {
232    name: &'static CStr,
233    mark: bool,
234    size: bool,
235    compact: bool,
236    free_immediately: bool,
237    wb_protected: bool,
238    frozen_shareable: bool,
239    phantom: PhantomData<T>,
240}
241
242/// Create a new [`DataTypeBuilder`].
243///
244/// `name` should be unique per wrapped type. It does not need to be a
245/// valid Ruby identifier.
246///
247/// `data_type_builder!(Example, "example")` is equivalent to
248/// `DataTypeBuilder::<Example>::new` with a `name` argument of `"example"` as
249/// a `'static CStr`.
250#[deprecated(note = "please use `DataTypeBuilder::<Example>::new(c\"example\")` instead")]
251#[macro_export]
252macro_rules! data_type_builder {
253    ($t:ty, $name:literal) => {
254        $crate::typed_data::DataTypeBuilder::<$t>::new(unsafe {
255            std::ffi::CStr::from_bytes_with_nul_unchecked(concat!($name, "\0").as_bytes())
256        })
257    };
258}
259
260impl<T> DataTypeBuilder<T>
261where
262    T: DataTypeFunctions,
263{
264    /// Create a new `DataTypeBuilder`.
265    ///
266    /// `name` should be unique per wrapped type. It does not need to be a
267    /// valid Ruby identifier.
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// use magnus::typed_data::DataTypeBuilder;
273    /// # use magnus::DataTypeFunctions;
274    /// # #[derive(DataTypeFunctions)]
275    /// # struct Example();
276    ///
277    /// DataTypeBuilder::<Example>::new(c"example");
278    /// ```
279    pub const fn new(name: &'static CStr) -> Self {
280        Self {
281            name,
282            mark: false,
283            size: false,
284            compact: false,
285            free_immediately: false,
286            wb_protected: false,
287            frozen_shareable: false,
288            phantom: PhantomData,
289        }
290    }
291
292    /// Enable using the the `mark` function from `<T as DataTypeFunctions>`.
293    pub const fn mark(mut self) -> Self {
294        self.mark = true;
295        self
296    }
297
298    /// Enable using the the `size` function from `<T as DataTypeFunctions>`.
299    pub const fn size(mut self) -> Self {
300        self.size = true;
301        self
302    }
303
304    /// Enable using the the `compact` function from `<T as DataTypeFunctions>`.
305    pub const fn compact(mut self) -> Self {
306        self.compact = true;
307        self
308    }
309
310    /// Enable the 'free_immediately' flag.
311    ///
312    /// This is safe to do as long as the `<T as DataTypeFunctions>::free`
313    /// function or `T`'s drop function don't call Ruby in any way.
314    ///
315    /// If safe this should be enabled as this performs better and is more
316    /// memory efficient.
317    pub const fn free_immediately(mut self) -> Self {
318        self.free_immediately = true;
319        self
320    }
321
322    /// Enable the 'write barrier protected' flag.
323    ///
324    /// Types that contain Ruby values by default do not participate in
325    /// generational GC (they are scanned every GC). This flag asserts all
326    /// operations that write Ruby values to this type are protected with
327    /// write barriers (see [`Writebarrier::writebarrier`]) so this
328    /// type can participate in generational GC.
329    ///
330    /// The write barrier is hard to get right. Magnus recommends you do not use
331    /// this flag.
332    pub const fn wb_protected(mut self) -> Self {
333        self.wb_protected = true;
334        self
335    }
336
337    /// Consume the builder and create a DataType.
338    pub const fn build(self) -> DataType {
339        let mut flags = 0_usize as VALUE;
340        if self.free_immediately {
341            flags |= RUBY_TYPED_FREE_IMMEDIATELY as VALUE;
342        }
343        if self.wb_protected || !self.mark {
344            flags |= RUBY_TYPED_WB_PROTECTED as VALUE;
345        }
346        if self.frozen_shareable {
347            flags |= rbimpl_typeddata_flags::RUBY_TYPED_FROZEN_SHAREABLE as VALUE;
348        }
349        let dmark = if self.mark {
350            Some(T::extern_mark as _)
351        } else {
352            None
353        };
354        let dfree = Some(T::extern_free as _);
355        let dsize = if self.size {
356            Some(T::extern_size as _)
357        } else {
358            None
359        };
360        let dcompact = if self.compact {
361            Some(T::extern_compact as _)
362        } else {
363            None
364        };
365        DataType(rb_data_type_t {
366            wrap_struct_name: self.name.as_ptr() as _,
367            function: rb_data_type_struct__bindgen_ty_1 {
368                dmark,
369                dfree,
370                dsize,
371                dcompact,
372                #[cfg(ruby_lt_4_1)]
373                reserved: [ptr::null_mut(); 1],
374                #[cfg(ruby_gte_4_1)]
375                // Ruby 4.1 expands rtypeddata's reserved slots; keep in sync with
376                // https://github.com/ruby/ruby/blob/master/include/ruby/internal/core/rtypeddata.h
377                reserved: [ptr::null_mut(); 7],
378                #[cfg(ruby_gte_4_1)]
379                handle_weak_references: None,
380            },
381            parent: ptr::null(),
382            data: ptr::null_mut(),
383            flags,
384        })
385    }
386}
387
388impl<T> DataTypeBuilder<T>
389where
390    T: DataTypeFunctions + Sync,
391{
392    /// Enable the 'frozen_shareable' flag.
393    ///
394    /// Set this if your type is thread safe when the Ruby wrapper object is
395    /// frozen.
396    pub const fn frozen_shareable(mut self) -> Self {
397        self.frozen_shareable = true;
398        self
399    }
400}
401
402/// A trait for Rust types that can be used with the
403/// `rb_data_typed_object_wrap` API.
404///
405/// # Safety
406///
407/// This trait is unsafe to implement as the fields of [`DataType`] returned by
408/// [`TypedData::data_type`] control low level behaviour that can go very wrong
409/// if set incorrectly. Implementing this trait is the only way a [`DataType`]
410/// can be passed to Ruby and result in safety violations, [`DataType`] is
411/// otherwise safe (but useless) to create.
412///
413/// The [`TypedData`](`derive@crate::TypedData`) or [`wrap`](`crate::wrap`)
414/// macros can help implementing this trait more safely.
415pub unsafe trait TypedData
416where
417    Self: Send + Sized,
418{
419    /// Should return the class for the Ruby object wrapping the Rust type.
420    ///
421    /// This can be overridden on a case by case basis by implementing
422    /// [`TypedData::class_for`], but the result of this function will always
423    /// be used in error messages if a value fails to convert to `Self`.
424    ///
425    /// If using [`class_for`](Self::class_for) it is advised to have this
426    /// function return the superclass of those returned by `class_for`.
427    ///
428    /// # Examples
429    ///
430    /// ```
431    /// use magnus::{RClass, Ruby, TypedData, prelude::*, value::Lazy};
432    /// # use magnus::DataType;
433    ///
434    /// struct Example();
435    ///
436    /// unsafe impl TypedData for Example {
437    ///     fn class(ruby: &Ruby) -> RClass {
438    ///         static CLASS: Lazy<RClass> = Lazy::new(|ruby| {
439    ///             let class = ruby.define_class("Example", ruby.class_object()).unwrap();
440    ///             class.undef_default_alloc_func();
441    ///             class
442    ///         });
443    ///         ruby.get_inner(&CLASS)
444    ///     }
445    ///
446    ///     // ...
447    /// #   fn data_type() -> &'static DataType { unimplemented!() }
448    /// }
449    /// # Example();
450    /// ```
451    fn class(ruby: &Ruby) -> RClass;
452
453    /// Should return a static reference to a [`DataType`] with metadata about
454    /// the wrapped type.
455    ///
456    /// # Examples
457    ///
458    /// ```
459    /// use magnus::{DataType, DataTypeFunctions, TypedData, typed_data::DataTypeBuilder};
460    /// # use magnus::{RClass, Ruby};
461    ///
462    /// #[derive(DataTypeFunctions)]
463    /// struct Example();
464    ///
465    /// unsafe impl TypedData for Example {
466    /// #   fn class(_: &Ruby) -> RClass { unimplemented!() }
467    ///     // ...
468    ///
469    ///     fn data_type() -> &'static DataType {
470    ///         static DATA_TYPE: DataType = DataTypeBuilder::<Example>::new(c"example").build();
471    ///         &DATA_TYPE
472    ///     }
473    /// }
474    /// # Example();
475    /// ```
476    fn data_type() -> &'static DataType;
477
478    /// Used to customise the class wrapping a specific value of `Self`.
479    ///
480    /// The provided implementation simply returns the value of
481    /// [`TypedData::class`].
482    ///
483    /// The classes returned by this function must be subclasses of
484    /// `TypedData::class`. `TypedData::class` will always be used in error
485    /// messages if a value fails to convert to `Self`.
486    ///
487    /// See also [`Obj::wrap_as`]/[`RTypedData::wrap_as`].
488    ///
489    /// # Examples
490    ///
491    /// ```
492    /// use magnus::{RClass, Ruby, TypedData, prelude::*, value::Lazy};
493    /// # use magnus::DataType;
494    ///
495    /// enum Example {
496    ///     A,
497    ///     B,
498    /// }
499    ///
500    /// unsafe impl TypedData for Example {
501    /// #   fn class(_: &Ruby) -> RClass { unimplemented!() }
502    /// #   fn data_type() -> &'static DataType { unimplemented!() }
503    ///     // ...
504    ///
505    ///     fn class_for(ruby: &Ruby, value: &Self) -> RClass {
506    ///         static A: Lazy<RClass> = Lazy::new(|ruby| {
507    ///             let class = ruby.define_class("A", Example::class(ruby)).unwrap();
508    ///             class.undef_default_alloc_func();
509    ///             class
510    ///         });
511    ///         static B: Lazy<RClass> = Lazy::new(|ruby| {
512    ///             let class = ruby.define_class("B", Example::class(ruby)).unwrap();
513    ///             class.undef_default_alloc_func();
514    ///             class
515    ///         });
516    ///         match value {
517    ///             Self::A => ruby.get_inner(&A),
518    ///             Self::B => ruby.get_inner(&B),
519    ///         }
520    ///     }
521    /// }
522    /// # let _ = (Example::A, Example::B);
523    /// ```
524    #[allow(unused_variables)]
525    fn class_for(ruby: &Ruby, value: &Self) -> RClass {
526        Self::class(ruby)
527    }
528}
529
530impl<T> TryConvert for &T
531where
532    T: TypedData,
533{
534    fn try_convert(val: Value) -> Result<Self, Error> {
535        let handle = Ruby::get_with(val);
536        unsafe {
537            RTypedData::from_value(val)
538                .ok_or_else(|| {
539                    Error::new(
540                        handle.exception_type_error(),
541                        format!(
542                            "no implicit conversion of {} into {}",
543                            val.classname(),
544                            T::class(&handle)
545                        ),
546                    )
547                })?
548                .get_unconstrained()
549        }
550    }
551}
552
553// This impl causes rustc to recommend `TypedData` in places it would be much
554// more helpful to instead `IntoValue`, so tell it not to.
555#[diagnostic::do_not_recommend]
556impl<T> IntoValue for T
557where
558    T: TypedData,
559{
560    #[inline]
561    fn into_value_with(self, handle: &Ruby) -> Value {
562        handle.wrap(self).into_value_with(handle)
563    }
564}
565
566/// A Ruby Object wrapping a Rust type `T`.
567///
568/// This is a Value pointer to a RTypedData struct, Ruby’s internal
569/// representation of objects that wrap foreign types. Unlike [`RTypedData`] it
570/// tracks the Rust type it should contains and errors early in [`TryConvert`]
571/// if types don't match.
572///
573/// See the [`ReprValue`] and [`Object`] traits for additional methods
574/// available on this type. See [`Ruby`](Ruby#typed_dataobj) for methods to
575/// create a `typed_data::Obj`.
576#[repr(transparent)]
577pub struct Obj<T> {
578    inner: RTypedData,
579    phantom: PhantomData<T>,
580}
581
582impl<T> Copy for Obj<T> where T: TypedData {}
583
584impl<T> Clone for Obj<T>
585where
586    T: TypedData,
587{
588    fn clone(&self) -> Self {
589        *self
590    }
591}
592
593/// # `typed_data::Obj`
594///
595/// Functions to wrap Rust data in a Ruby object.
596///
597/// See also [`RTypedData`](Ruby#rtypeddata) and the [`typed_data::Obj`](Obj)
598/// type.
599impl Ruby {
600    /// Wrap the Rust type `T` in a Ruby object.
601    ///
602    /// # Examples
603    ///
604    /// ```
605    /// use magnus::{Error, Ruby, prelude::*};
606    ///
607    /// #[magnus::wrap(class = "Point")]
608    /// struct Point {
609    ///     x: isize,
610    ///     y: isize,
611    /// }
612    ///
613    /// fn example(ruby: &Ruby) -> Result<(), Error> {
614    ///     let point_class = ruby.define_class("Point", ruby.class_object())?;
615    ///
616    ///     let value = ruby.obj_wrap(Point { x: 4, y: 2 });
617    ///     assert!(value.is_kind_of(point_class));
618    ///
619    ///     Ok(())
620    /// }
621    /// # Ruby::init(example).unwrap();
622    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
623    /// ```
624    pub fn obj_wrap<T>(&self, data: T) -> Obj<T>
625    where
626        T: TypedData,
627    {
628        Obj {
629            inner: self.wrap(data),
630            phantom: PhantomData,
631        }
632    }
633
634    /// Wrap the Rust type `T` in a Ruby object that is an instance of the
635    /// given `class`.
636    ///
637    /// See also [`TypedData::class_for`].
638    ///
639    /// # Panics
640    ///
641    /// Panics if `class` is not a subclass of `<T as TypedData>::class()`.
642    ///
643    /// # Examples
644    ///
645    /// ```
646    /// use magnus::{Error, Ruby, prelude::*};
647    ///
648    /// #[magnus::wrap(class = "Point")]
649    /// struct Point {
650    ///     x: isize,
651    ///     y: isize,
652    /// }
653    ///
654    /// fn example(ruby: &Ruby) -> Result<(), Error> {
655    ///     let point_class = ruby.define_class("Point", ruby.class_object())?;
656    ///     let point_sub_class = ruby.define_class("SubPoint", point_class)?;
657    ///
658    ///     let value = ruby.obj_wrap_as(Point { x: 4, y: 2 }, point_sub_class);
659    ///     assert!(value.is_kind_of(point_sub_class));
660    ///     assert!(value.is_kind_of(point_class));
661    ///
662    ///     Ok(())
663    /// }
664    /// # Ruby::init(example).unwrap();
665    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
666    /// ```
667    ///
668    /// Allowing a wrapped type to be subclassed from Ruby:
669    ///
670    /// (note, in this example `Point` does not have and does not call the
671    /// `initialize` method, subclasses would need to override the class `new`
672    /// method rather than `initialize`)
673    ///
674    /// ```
675    /// use magnus::{Error, RClass, Ruby, Value, function, method, prelude::*, typed_data};
676    ///
677    /// #[magnus::wrap(class = "Point")]
678    /// struct Point {
679    ///     x: isize,
680    ///     y: isize,
681    /// }
682    ///
683    /// impl Point {
684    ///     fn new(ruby: &Ruby, class: RClass, x: isize, y: isize) -> typed_data::Obj<Self> {
685    ///         ruby.obj_wrap_as(Self { x, y }, class)
686    ///     }
687    /// }
688    ///
689    /// fn example(ruby: &Ruby) -> Result<(), Error> {
690    ///     let point_class = ruby.define_class("Point", ruby.class_object())?;
691    ///     point_class.define_singleton_method("new", method!(Point::new, 2))?;
692    ///     point_class
693    ///         .define_singleton_method("inherited", function!(RClass::undef_default_alloc_func, 1))?;
694    ///
695    ///     let value: Value = ruby.eval(
696    ///         r#"
697    ///           class SubPoint < Point
698    ///           end
699    ///           SubPoint.new(4, 2)
700    ///         "#,
701    ///     )?;
702    ///
703    ///     assert!(value.is_kind_of(ruby.class_object().const_get::<_, RClass>("SubPoint")?));
704    ///     assert!(value.is_kind_of(point_class));
705    ///
706    ///     Ok(())
707    /// }
708    /// # Ruby::init(example).unwrap();
709    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
710    /// ```
711    pub fn obj_wrap_as<T>(&self, data: T, class: RClass) -> Obj<T>
712    where
713        T: TypedData,
714    {
715        Obj {
716            inner: self.wrap_as(data, class),
717            phantom: PhantomData,
718        }
719    }
720}
721
722impl<T> Obj<T>
723where
724    T: TypedData,
725{
726    /// Wrap the Rust type `T` in a Ruby object.
727    ///
728    /// # Panics
729    ///
730    /// Panics if called from a non-Ruby thread. See [`Ruby::obj_wrap`] for the
731    /// non-panicking version.
732    ///
733    /// # Examples
734    ///
735    /// ```
736    /// # #![allow(deprecated)]
737    /// use magnus::{class, define_class, prelude::*, typed_data};
738    /// # let _cleanup = unsafe { magnus::embed::init() };
739    ///
740    /// #[magnus::wrap(class = "Point")]
741    /// struct Point {
742    ///     x: isize,
743    ///     y: isize,
744    /// }
745    ///
746    /// let point_class = define_class("Point", class::object()).unwrap();
747    ///
748    /// let value = typed_data::Obj::wrap(Point { x: 4, y: 2 });
749    /// assert!(value.is_kind_of(point_class));
750    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
751    /// ```
752    #[deprecated(note = "please use `Ruby::obj_wrap` instead")]
753    #[cfg(feature = "old-api")]
754    #[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
755    #[inline]
756    pub fn wrap(data: T) -> Self {
757        get_ruby!().obj_wrap(data)
758    }
759
760    /// Wrap the Rust type `T` in a Ruby object that is an instance of the
761    /// given `class`.
762    ///
763    /// See also [`TypedData::class_for`].
764    ///
765    /// # Panics
766    ///
767    /// Panics if `class` is not a subclass of `<T as TypedData>::class()`, or
768    /// if called from a non-Ruby thread. See [`Ruby::obj_wrap_as`] for a
769    /// version that can not be called from a non-Ruby thread, so will not
770    /// panic for that reason.
771    ///
772    /// # Examples
773    ///
774    /// ```
775    /// # #![allow(deprecated)]
776    /// use magnus::{class, define_class, prelude::*, typed_data};
777    /// # let _cleanup = unsafe { magnus::embed::init() };
778    ///
779    /// #[magnus::wrap(class = "Point")]
780    /// struct Point {
781    ///     x: isize,
782    ///     y: isize,
783    /// }
784    ///
785    /// let point_class = define_class("Point", class::object()).unwrap();
786    /// let point_sub_class = define_class("SubPoint", point_class).unwrap();
787    ///
788    /// let value = typed_data::Obj::wrap_as(Point { x: 4, y: 2 }, point_sub_class);
789    /// assert!(value.is_kind_of(point_sub_class));
790    /// assert!(value.is_kind_of(point_class));
791    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
792    /// ```
793    ///
794    /// Allowing a wrapped type to be subclassed from Ruby:
795    ///
796    /// (note, in this example `Point` does not have and does not call
797    /// the `initialize` method, subclasses would need to override the class
798    /// `new` method rather than `initialize`)
799    ///
800    /// ```
801    /// # #![allow(deprecated)]
802    /// use magnus::{
803    ///     RClass, Value, class, define_class, eval, function, method, prelude::*, typed_data,
804    /// };
805    /// # let _cleanup = unsafe { magnus::embed::init() };
806    ///
807    /// #[magnus::wrap(class = "Point")]
808    /// struct Point {
809    ///     x: isize,
810    ///     y: isize,
811    /// }
812    ///
813    /// impl Point {
814    ///     fn new(class: RClass, x: isize, y: isize) -> typed_data::Obj<Self> {
815    ///         typed_data::Obj::wrap_as(Self { x, y }, class)
816    ///     }
817    /// }
818    /// let point_class = define_class("Point", class::object()).unwrap();
819    /// point_class
820    ///     .define_singleton_method("new", method!(Point::new, 2))
821    ///     .unwrap();
822    /// point_class
823    ///     .define_singleton_method("inherited", function!(RClass::undef_default_alloc_func, 1))
824    ///     .unwrap();
825    ///
826    /// let value: Value = eval(
827    ///     r#"
828    ///       class SubPoint < Point
829    ///       end
830    ///       SubPoint.new(4, 2)
831    ///     "#,
832    /// )
833    /// .unwrap();
834    ///
835    /// assert!(value.is_kind_of(class::object().const_get::<_, RClass>("SubPoint").unwrap()));
836    /// assert!(value.is_kind_of(point_class));
837    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
838    /// ```
839    #[deprecated(note = "please use `Ruby::obj_wrap_as` instead")]
840    #[cfg(feature = "old-api")]
841    #[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
842    #[inline]
843    pub fn wrap_as(data: T, class: RClass) -> Self {
844        get_ruby!().obj_wrap_as(data, class)
845    }
846
847    /// Get the raw pointer to the Rust type wrapped in the Ruby object `obj`.
848    ///
849    /// While it is safe to acquire this pointer it is unsafe to use. You must
850    /// ensure the Ruby object is kept alive, and the pointer is not aliased or
851    /// written to concurrently.
852    pub fn as_ptr(obj: Self) -> *mut T {
853        obj.inner.as_ptr().unwrap()
854    }
855}
856
857impl<T> Deref for Obj<T>
858where
859    T: TypedData,
860{
861    type Target = T;
862
863    /// Dereference to the Rust type wrapped in the Ruby object `self`.
864    ///
865    /// # Examples
866    ///
867    /// ```
868    /// use magnus::{Error, Ruby};
869    ///
870    /// #[magnus::wrap(class = "Point")]
871    /// #[derive(Debug, PartialEq, Eq)]
872    /// struct Point {
873    ///     x: isize,
874    ///     y: isize,
875    /// }
876    ///
877    /// fn example(ruby: &Ruby) -> Result<(), Error> {
878    ///     ruby.define_class("Point", ruby.class_object())?;
879    ///     let value = ruby.obj_wrap(Point { x: 4, y: 2 });
880    ///
881    ///     assert_eq!(&*value, &Point { x: 4, y: 2 });
882    ///
883    ///     Ok(())
884    /// }
885    /// # Ruby::init(example).unwrap()
886    /// ```
887    fn deref(&self) -> &Self::Target {
888        // Since we've already validated the inner during `TryConvert` via `RTypedData::get`, we
889        // can skip the extra checks and libruby calls and just access the data directly.
890        unsafe {
891            let data_ptr = RTYPEDDATA_GET_DATA(self.inner.as_rb_value()) as *mut Self::Target;
892            &*data_ptr
893        }
894    }
895}
896
897impl<T> fmt::Display for Obj<T>
898where
899    T: TypedData,
900{
901    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
902        write!(f, "{}", unsafe { self.to_s_infallible() })
903    }
904}
905
906impl<T> fmt::Debug for Obj<T>
907where
908    T: TypedData,
909{
910    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
911        write!(f, "{}", self.inspect())
912    }
913}
914
915impl<T> IntoValue for Obj<T>
916where
917    T: TypedData,
918{
919    #[inline]
920    fn into_value_with(self, handle: &Ruby) -> Value {
921        self.inner.into_value_with(handle)
922    }
923}
924
925impl<T> From<Obj<T>> for RTypedData
926where
927    T: TypedData,
928{
929    fn from(val: Obj<T>) -> Self {
930        val.inner
931    }
932}
933
934impl<T> Object for Obj<T> where T: TypedData {}
935
936unsafe impl<T> private::ReprValue for Obj<T> where T: TypedData {}
937
938impl<T> ReprValue for Obj<T> where T: TypedData {}
939
940impl<T> TryConvert for Obj<T>
941where
942    T: TypedData,
943{
944    fn try_convert(val: Value) -> Result<Self, Error> {
945        let handle = Ruby::get_with(val);
946        let inner = RTypedData::from_value(val).ok_or_else(|| {
947            Error::new(
948                handle.exception_type_error(),
949                format!(
950                    "no implicit conversion of {} into {}",
951                    unsafe { val.classname() },
952                    T::class(&handle)
953                ),
954            )
955        })?;
956
957        // check it really does contain a T
958        inner.get::<T>()?;
959
960        Ok(Self {
961            inner,
962            phantom: PhantomData,
963        })
964    }
965}
966
967/// A trait for types that can be used with the `rb_gc_writebarrier` API.
968pub trait Writebarrier: ReprValue {
969    /// Inform Ruby that `self` contains a reference to `new`.
970    ///
971    /// If you have a Rust type that contains Ruby values, and itself is
972    /// wrapped as a Ruby object, and you choose to enable the `wb_protected`
973    /// flag so that it can participate in generational GC then all operations
974    /// that add a Ruby value to your data type must call this function.
975    ///
976    /// The write barrier is hard to get right. Magnus recommends you do not
977    /// enable the `wb_protected` flag, and thus you don't need to use this function.
978    ///
979    /// # Examples
980    ///
981    /// ```
982    /// use std::cell::RefCell;
983    ///
984    /// use magnus::{
985    ///     DataTypeFunctions, Error, Ruby, TypedData, Value, function, gc, method,
986    ///     prelude::*,
987    ///     typed_data::{Obj, Writebarrier},
988    ///     value::Opaque,
989    /// };
990    ///
991    /// #[derive(TypedData)]
992    /// #[magnus(class = "MyVec", free_immediately, mark, wb_protected)]
993    /// struct MyVec {
994    ///     values: RefCell<Vec<Opaque<Value>>>,
995    /// }
996    ///
997    /// impl DataTypeFunctions for MyVec {
998    ///     fn mark(&self, marker: &gc::Marker) {
999    ///         marker.mark_slice(self.values.borrow().as_slice());
1000    ///     }
1001    /// }
1002    ///
1003    /// impl MyVec {
1004    ///     fn new() -> Self {
1005    ///         Self {
1006    ///             values: RefCell::new(Vec::new()),
1007    ///         }
1008    ///     }
1009    ///
1010    ///     fn push(rb_self: Obj<Self>, val: Value) -> Obj<Self> {
1011    ///         rb_self.values.borrow_mut().push(val.into());
1012    ///         rb_self.writebarrier(rb_self);
1013    ///         rb_self
1014    ///     }
1015    /// }
1016    ///
1017    /// fn example(ruby: &Ruby) -> Result<(), Error> {
1018    ///     let class = ruby.define_class("MyVec", ruby.class_object())?;
1019    ///     class.define_singleton_method("new", function!(MyVec::new, 0))?;
1020    ///     class.define_method("push", method!(MyVec::push, 1))?;
1021    ///
1022    ///     let _: Value = ruby.eval(
1023    ///         r#"
1024    ///             vec = MyVec.new
1025    ///             vec.push("test")
1026    ///             vec.push("example")
1027    ///         "#,
1028    ///     )?;
1029    ///
1030    ///     Ok(())
1031    /// }
1032    /// # Ruby::init(example).unwrap();
1033    /// ```
1034    fn writebarrier<T>(&self, young: T)
1035    where
1036        T: Mark,
1037    {
1038        unsafe { rb_gc_writebarrier(self.as_rb_value(), young.raw_with(&Ruby::get_with(*self))) };
1039    }
1040
1041    /// Opts `self` out of generational GC / write barrier protection.
1042    ///
1043    /// After calling this function `self` will not participate in generational
1044    /// GC and will always be scanned during a GC.
1045    /// See [`RTypedData::writebarrier`].
1046    fn writebarrier_unprotect<T, U>(&self) {
1047        unsafe { rb_gc_writebarrier_unprotect(self.as_rb_value()) };
1048    }
1049}
1050
1051impl Writebarrier for RTypedData {}
1052
1053impl<T> Writebarrier for Obj<T> where T: TypedData {}
1054
1055/// Trait for a Ruby-compatible `#hash` method.
1056///
1057/// Automatically implemented for any type implementing [`std::hash::Hash`].
1058///
1059/// See also [`Dup`], [`Inspect`], [`IsEql`], and [`typed_data::Cmp`](Cmp).
1060///
1061/// # Examples
1062///
1063/// ```
1064/// use std::hash::Hasher;
1065///
1066/// use magnus::{
1067///     DataTypeFunctions, Error, Ruby, TypedData, Value, function, gc, method, prelude::*,
1068///     typed_data, value::Opaque,
1069/// };
1070///
1071/// #[derive(TypedData)]
1072/// #[magnus(class = "Pair", free_immediately, mark)]
1073/// struct Pair {
1074///     #[magnus(opaque_attr_reader)]
1075///     a: Opaque<Value>,
1076///     #[magnus(opaque_attr_reader)]
1077///     b: Opaque<Value>,
1078/// }
1079///
1080/// impl Pair {
1081///     fn new(a: Value, b: Value) -> Self {
1082///         Self {
1083///             a: a.into(),
1084///             b: b.into(),
1085///         }
1086///     }
1087/// }
1088///
1089/// impl DataTypeFunctions for Pair {
1090///     fn mark(&self, marker: &gc::Marker) {
1091///         marker.mark(self.a);
1092///         marker.mark(self.b);
1093///     }
1094/// }
1095///
1096/// impl std::hash::Hash for Pair {
1097///     fn hash<H: Hasher>(&self, state: &mut H) {
1098///         state.write_i64(
1099///             self.a()
1100///                 .hash()
1101///                 .expect("#hash should not fail")
1102///                 .to_i64()
1103///                 .expect("#hash result guaranteed to be <= i64"),
1104///         );
1105///         state.write_i64(
1106///             self.b()
1107///                 .hash()
1108///                 .expect("#hash should not fail")
1109///                 .to_i64()
1110///                 .expect("#hash result guaranteed to be <= i64"),
1111///         );
1112///     }
1113/// }
1114///
1115/// impl PartialEq for Pair {
1116///     fn eq(&self, other: &Self) -> bool {
1117///         self.a().eql(other.a()).unwrap_or(false) && self.b().eql(other.b()).unwrap_or(false)
1118///     }
1119/// }
1120///
1121/// impl Eq for Pair {}
1122///
1123/// fn example(ruby: &Ruby) -> Result<(), Error> {
1124///     let class = ruby.define_class("Pair", ruby.class_object())?;
1125///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1126///     class.define_method("hash", method!(<Pair as typed_data::Hash>::hash, 0))?;
1127///     class.define_method("eql?", method!(<Pair as typed_data::IsEql>::is_eql, 1))?;
1128///
1129///     let a = Pair::new(
1130///         ruby.str_new("foo").as_value(),
1131///         ruby.integer_from_i64(1).as_value(),
1132///     );
1133///     let hash = ruby.hash_new();
1134///     hash.aset(a, "test value")?;
1135///
1136///     let b = Pair::new(
1137///         ruby.str_new("foo").as_value(),
1138///         ruby.integer_from_i64(1).as_value(),
1139///     );
1140///     assert_eq!("test value", hash.fetch::<_, String>(b)?);
1141///
1142///     let c = Pair::new(
1143///         ruby.str_new("bar").as_value(),
1144///         ruby.integer_from_i64(2).as_value(),
1145///     );
1146///     assert!(hash.get(c).is_none());
1147///
1148///     Ok(())
1149/// }
1150/// # Ruby::init(example).unwrap()
1151/// ```
1152pub trait Hash {
1153    // Docs at trait level.
1154    #![allow(missing_docs)]
1155    fn hash(&self) -> i64;
1156}
1157
1158impl<T> Hash for T
1159where
1160    T: std::hash::Hash,
1161{
1162    fn hash(&self) -> i64 {
1163        let mut hasher = DefaultHasher::new();
1164        std::hash::Hash::hash(self, &mut hasher);
1165        // Ensure the Rust usize hash converts nicely to Ruby's expected range
1166        // if we return usize it'd truncate to 0 for anything negative.
1167        hasher.finish() as i64
1168    }
1169}
1170
1171/// Trait for a Ruby-compatible `#eql?` method.
1172///
1173/// Automatically implemented for any type implementing [`Eq`] and
1174/// [`TryConvert`].
1175///
1176/// See also [`Dup`], [`Inspect`], [`typed_data::Cmp`](Cmp), and
1177/// [`typed_data::Hash`](Hash).
1178///
1179/// # Examples
1180///
1181/// ```
1182/// use std::hash::Hasher;
1183///
1184/// use magnus::{
1185///     DataTypeFunctions, Error, Ruby, TypedData, Value, function, gc, method, prelude::*,
1186///     typed_data, value::Opaque,
1187/// };
1188///
1189/// #[derive(TypedData)]
1190/// #[magnus(class = "Pair", free_immediately, mark)]
1191/// struct Pair {
1192///     #[magnus(opaque_attr_reader)]
1193///     a: Opaque<Value>,
1194///     #[magnus(opaque_attr_reader)]
1195///     b: Opaque<Value>,
1196/// }
1197///
1198/// impl Pair {
1199///     fn new(a: Value, b: Value) -> Self {
1200///         Self {
1201///             a: a.into(),
1202///             b: b.into(),
1203///         }
1204///     }
1205/// }
1206///
1207/// impl DataTypeFunctions for Pair {
1208///     fn mark(&self, marker: &gc::Marker) {
1209///         marker.mark(self.a);
1210///         marker.mark(self.b);
1211///     }
1212/// }
1213///
1214/// impl std::hash::Hash for Pair {
1215///     fn hash<H: Hasher>(&self, state: &mut H) {
1216///         state.write_i64(
1217///             self.a()
1218///                 .hash()
1219///                 .expect("#hash should not fail")
1220///                 .to_i64()
1221///                 .expect("#hash result guaranteed to be <= i64"),
1222///         );
1223///         state.write_i64(
1224///             self.b()
1225///                 .hash()
1226///                 .expect("#hash should not fail")
1227///                 .to_i64()
1228///                 .expect("#hash result guaranteed to be <= i64"),
1229///         );
1230///     }
1231/// }
1232///
1233/// impl PartialEq for Pair {
1234///     fn eq(&self, other: &Self) -> bool {
1235///         self.a().eql(other.a()).unwrap_or(false) && self.b().eql(other.b()).unwrap_or(false)
1236///     }
1237/// }
1238///
1239/// impl Eq for Pair {}
1240///
1241/// fn example(ruby: &Ruby) -> Result<(), Error> {
1242///     let class = ruby.define_class("Pair", ruby.class_object())?;
1243///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1244///     class.define_method("hash", method!(<Pair as typed_data::Hash>::hash, 0))?;
1245///     class.define_method("eql?", method!(<Pair as typed_data::IsEql>::is_eql, 1))?;
1246///
1247///     let a = Pair::new(
1248///         ruby.str_new("foo").as_value(),
1249///         ruby.integer_from_i64(1).as_value(),
1250///     );
1251///     let hash = ruby.hash_new();
1252///     hash.aset(a, "test value")?;
1253///
1254///     let b = Pair::new(
1255///         ruby.str_new("foo").as_value(),
1256///         ruby.integer_from_i64(1).as_value(),
1257///     );
1258///     assert_eq!("test value", hash.fetch::<_, String>(b)?);
1259///
1260///     let c = Pair::new(
1261///         ruby.str_new("bar").as_value(),
1262///         ruby.integer_from_i64(2).as_value(),
1263///     );
1264///     assert!(hash.get(c).is_none());
1265///
1266///     Ok(())
1267/// }
1268/// # Ruby::init(example).unwrap()
1269/// ```
1270pub trait IsEql {
1271    // Docs at trait level.
1272    #![allow(missing_docs)]
1273    fn is_eql(&self, other: Value) -> bool;
1274}
1275
1276impl<'a, T> IsEql for T
1277where
1278    T: Eq + 'a,
1279    &'a T: TryConvert,
1280{
1281    fn is_eql(&self, other: Value) -> bool {
1282        <&'a T>::try_convert(other)
1283            .map(|o| self == o)
1284            .unwrap_or(false)
1285    }
1286}
1287
1288/// Trait for a Ruby-compatible `#<=>` method.
1289///
1290/// Automatically implemented for any type implementing [`PartialOrd`] and
1291/// [`TryConvert`].
1292///
1293/// See also [`Dup`], [`Inspect`], [`IsEql`] and [`typed_data::Hash`](Hash).
1294///
1295/// # Examples
1296///
1297/// ```
1298/// use std::cmp::Ordering;
1299///
1300/// use magnus::{
1301///     DataTypeFunctions, Error, Module, Ruby, TypedData, Value, function, gc, method, prelude::*,
1302///     rb_assert, typed_data, value::Opaque,
1303/// };
1304///
1305/// #[derive(TypedData)]
1306/// #[magnus(class = "Pair", free_immediately, mark)]
1307/// struct Pair {
1308///     #[magnus(opaque_attr_reader)]
1309///     a: Opaque<Value>,
1310///     #[magnus(opaque_attr_reader)]
1311///     b: Opaque<Value>,
1312/// }
1313///
1314/// impl Pair {
1315///     fn new(a: Value, b: Value) -> Self {
1316///         Self {
1317///             a: a.into(),
1318///             b: b.into(),
1319///         }
1320///     }
1321/// }
1322///
1323/// impl DataTypeFunctions for Pair {
1324///     fn mark(&self, marker: &gc::Marker) {
1325///         marker.mark(self.a);
1326///         marker.mark(self.b);
1327///     }
1328/// }
1329///
1330/// impl PartialEq for Pair {
1331///     fn eq(&self, other: &Self) -> bool {
1332///         self.a().eql(other.a()).unwrap_or(false) && self.b().eql(other.b()).unwrap_or(false)
1333///     }
1334/// }
1335///
1336/// impl PartialOrd for Pair {
1337///     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1338///         let a = self
1339///             .a()
1340///             .funcall("<=>", (other.a(),))
1341///             .ok()
1342///             .map(|o: i64| o.cmp(&0))?;
1343///         match a {
1344///             Ordering::Less | Ordering::Greater => Some(a),
1345///             Ordering::Equal => self
1346///                 .b()
1347///                 .funcall("<=>", (other.b(),))
1348///                 .ok()
1349///                 .map(|o: i64| o.cmp(&0)),
1350///         }
1351///     }
1352/// }
1353///
1354/// fn example(ruby: &Ruby) -> Result<(), Error> {
1355///     let class = ruby.define_class("Pair", ruby.class_object())?;
1356///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1357///     class.define_method("<=>", method!(<Pair as typed_data::Cmp>::cmp, 1))?;
1358///     class.include_module(ruby.module_comparable())?;
1359///
1360///     let a = Pair::new(
1361///         ruby.str_new("foo").as_value(),
1362///         ruby.integer_from_i64(1).as_value(),
1363///     );
1364///     let b = Pair::new(
1365///         ruby.str_new("foo").as_value(),
1366///         ruby.integer_from_i64(2).as_value(),
1367///     );
1368///     rb_assert!(ruby, "a < b", a, b);
1369///
1370///     let b = Pair::new(
1371///         ruby.str_new("foo").as_value(),
1372///         ruby.integer_from_i64(2).as_value(),
1373///     );
1374///     let c = Pair::new(
1375///         ruby.str_new("bar").as_value(),
1376///         ruby.integer_from_i64(1).as_value(),
1377///     );
1378///     rb_assert!(ruby, "b > c", b, c);
1379///
1380///     let a = Pair::new(
1381///         ruby.str_new("foo").as_value(),
1382///         ruby.integer_from_i64(1).as_value(),
1383///     );
1384///     let b = Pair::new(
1385///         ruby.str_new("foo").as_value(),
1386///         ruby.integer_from_i64(2).as_value(),
1387///     );
1388///     rb_assert!(ruby, "(a <=> b) == -1", a, b);
1389///
1390///     Ok(())
1391/// }
1392/// # Ruby::init(example).unwrap()
1393/// ```
1394pub trait Cmp {
1395    // Docs at trait level.
1396    #![allow(missing_docs)]
1397    fn cmp(&self, other: Value) -> Option<i64>;
1398}
1399
1400impl<'a, T> Cmp for T
1401where
1402    T: PartialOrd + 'a,
1403    &'a T: TryConvert,
1404{
1405    fn cmp(&self, other: Value) -> Option<i64> {
1406        <&'a T>::try_convert(other)
1407            .ok()
1408            .and_then(|o| self.partial_cmp(o))
1409            .map(|o| o as i64)
1410    }
1411}
1412
1413/// Trait for a Ruby-compatible `#inspect` method.
1414///
1415/// Automatically implemented for any type implementing [`Debug`].
1416///
1417/// See also [`Dup`], [`IsEql`], [`typed_data::Cmp`](Cmp), and
1418/// [`typed_data::Hash`](Hash).
1419///
1420/// # Examples
1421///
1422/// ```
1423/// use std::fmt;
1424///
1425/// use magnus::{
1426///     DataTypeFunctions, Error, Ruby, TypedData, Value, function, gc, method, prelude::*,
1427///     rb_assert, typed_data, value::Opaque,
1428/// };
1429///
1430/// #[derive(TypedData)]
1431/// #[magnus(class = "Pair", free_immediately, mark)]
1432/// struct Pair {
1433///     #[magnus(opaque_attr_reader)]
1434///     a: Opaque<Value>,
1435///     #[magnus(opaque_attr_reader)]
1436///     b: Opaque<Value>,
1437/// }
1438///
1439/// impl Pair {
1440///     fn new(a: Value, b: Value) -> Self {
1441///         Self {
1442///             a: a.into(),
1443///             b: b.into(),
1444///         }
1445///     }
1446/// }
1447///
1448/// impl DataTypeFunctions for Pair {
1449///     fn mark(&self, marker: &gc::Marker) {
1450///         marker.mark(self.a);
1451///         marker.mark(self.b);
1452///     }
1453/// }
1454///
1455/// impl fmt::Debug for Pair {
1456///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1457///         f.debug_struct("Pair")
1458///             .field("a", &self.a())
1459///             .field("b", &self.b())
1460///             .finish()
1461///     }
1462/// }
1463///
1464/// fn example(ruby: &Ruby) -> Result<(), Error> {
1465///     let class = ruby.define_class("Pair", ruby.class_object())?;
1466///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1467///     class.define_method(
1468///         "inspect",
1469///         method!(<Pair as typed_data::Inspect>::inspect, 0),
1470///     )?;
1471///
1472///     let pair = Pair::new(
1473///         ruby.str_new("foo").as_value(),
1474///         ruby.integer_from_i64(1).as_value(),
1475///     );
1476///     rb_assert!(ruby, r#"pair.inspect == "Pair { a: \"foo\", b: 1 }""#, pair);
1477///
1478///     Ok(())
1479/// }
1480/// # Ruby::init(example).unwrap()
1481/// ```
1482pub trait Inspect {
1483    // Docs at trait level.
1484    #![allow(missing_docs)]
1485    fn inspect(&self) -> String;
1486}
1487
1488impl<T> Inspect for T
1489where
1490    T: fmt::Debug,
1491{
1492    fn inspect(&self) -> String {
1493        format!("{:?}", self)
1494    }
1495}
1496
1497/// Trait for a Ruby-compatible `#dup` and `#clone` methods.
1498///
1499/// Automatically implemented for any type implementing [`Clone`].
1500///
1501/// See also [`Inspect`], [`IsEql`], [`typed_data::Cmp`](Cmp), and
1502/// [`typed_data::Hash`](Hash).
1503///
1504/// # Examples
1505///
1506/// ```
1507/// use magnus::{
1508///     DataTypeFunctions, Error, Ruby, TypedData, Value, function, gc, method, prelude::*,
1509///     rb_assert, typed_data, value::Opaque,
1510/// };
1511///
1512/// #[derive(TypedData, Clone)]
1513/// #[magnus(class = "Pair", free_immediately, mark)]
1514/// struct Pair {
1515///     a: Opaque<Value>,
1516///     b: Opaque<Value>,
1517/// }
1518///
1519/// impl Pair {
1520///     fn new(a: Value, b: Value) -> Self {
1521///         Self {
1522///             a: a.into(),
1523///             b: b.into(),
1524///         }
1525///     }
1526/// }
1527///
1528/// impl DataTypeFunctions for Pair {
1529///     fn mark(&self, marker: &gc::Marker) {
1530///         marker.mark(self.a);
1531///         marker.mark(self.b);
1532///     }
1533/// }
1534///
1535/// fn example(ruby: &Ruby) -> Result<(), Error> {
1536///     let class = ruby.define_class("Pair", ruby.class_object())?;
1537///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1538///     class.define_method("dup", method!(<Pair as typed_data::Dup>::dup, 0))?;
1539///     class.define_method("clone", method!(<Pair as typed_data::Dup>::clone, -1))?;
1540///
1541///     let a = Pair::new(
1542///         ruby.str_new("foo").as_value(),
1543///         ruby.integer_from_i64(1).as_value(),
1544///     );
1545///     rb_assert!(ruby, "b = a.dup; a.object_id != b.object_id", a);
1546///
1547///     Ok(())
1548/// }
1549/// # Ruby::init(example).unwrap()
1550/// ```
1551pub trait Dup: Sized {
1552    // Docs at trait level.
1553    #![allow(missing_docs)]
1554    fn dup(&self) -> Self;
1555    fn clone(rbself: Obj<Self>, args: &[Value]) -> Result<Obj<Self>, Error>;
1556}
1557
1558impl<T> Dup for T
1559where
1560    T: Clone + TypedData,
1561{
1562    fn dup(&self) -> Self {
1563        self.clone()
1564    }
1565
1566    fn clone(rbself: Obj<Self>, args: &[Value]) -> Result<Obj<Self>, Error> {
1567        let args = scan_args::<(), (), (), (), _, ()>(args)?;
1568        let kwargs =
1569            get_kwargs::<_, (), (Option<Option<bool>>,), ()>(args.keywords, &[], &["freeze"])?;
1570        let (freeze,) = kwargs.optional;
1571        let freeze = freeze.flatten();
1572
1573        let clone = Ruby::get_with(rbself).obj_wrap((*rbself).clone());
1574        let class_clone = unsafe { rb_singleton_class_clone(rbself.as_rb_value()) };
1575        unsafe { rb_obj_reveal(clone.as_rb_value(), class_clone) };
1576        unsafe { rb_singleton_class_attached(class_clone, clone.as_rb_value()) };
1577        match freeze {
1578            Some(true) => clone.freeze(),
1579            None if rbself.is_frozen() => clone.freeze(),
1580            _ => (),
1581        }
1582        Ok(clone)
1583    }
1584}