1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Module for registering boxed types for Rust types.
5
6use crate::{prelude::*, translate::*};
7
8// rustdoc-stripper-ignore-next
9/// Trait for defining boxed types.
10///
11/// Links together the type name with the type itself.
12///
13/// See [`register_boxed_type`] for registering an implementation of this trait
14/// with the type system.
15///
16/// [`register_boxed_type`]: fn.register_boxed_type.html
17pub trait BoxedType: StaticType + Clone + Sized + 'static {
18 // rustdoc-stripper-ignore-next
19 /// Boxed type name.
20 ///
21 /// This must be unique in the whole process.
22 const NAME: &'static str;
23}
24
25// rustdoc-stripper-ignore-next
26/// Register a boxed `glib::Type` ID for `T`.
27///
28/// This must be called only once and will panic on a second call.
29///
30/// See [`Boxed!`] for defining a function that ensures that
31/// this is only called once and returns the type id.
32///
33/// [`Boxed!`]: ../../derive.Boxed.html
34pub fn register_boxed_type<T: BoxedType>() -> crate::Type {
35 unsafe extern "C" fn boxed_copy<T: BoxedType>(v: ffi::gpointer) -> ffi::gpointer {
36 let v = &*(v as *mut T);
37 let copy = Box::new(v.clone());
38
39 Box::into_raw(copy) as ffi::gpointer
40 }
41 unsafe extern "C" fn boxed_free<T: BoxedType>(v: ffi::gpointer) {
42 let v = v as *mut T;
43 let _ = Box::from_raw(v);
44 }
45 unsafe {
46 use std::ffi::CString;
47
48 let type_name = CString::new(T::NAME).unwrap();
49 assert_eq!(
50 gobject_ffi::g_type_from_name(type_name.as_ptr()),
51 gobject_ffi::G_TYPE_INVALID,
52 "Type {} has already been registered",
53 type_name.to_str().unwrap()
54 );
55
56 let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
57 type_name.as_ptr(),
58 Some(boxed_copy::<T>),
59 Some(boxed_free::<T>),
60 ));
61 assert!(type_.is_valid());
62
63 type_
64 }
65}
66
67#[cfg(test)]
68mod test {
69 // We rename the current crate as glib, since the macros in glib-macros
70 // generate the glib namespace through the crate_ident_new utility,
71 // and that returns `glib` (and not `crate`) when called inside the glib crate
72 use crate as glib;
73 use crate::prelude::*;
74 use crate::translate::{FromGlibPtrBorrow, FromGlibPtrFull, IntoGlibPtr};
75
76 #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
77 #[boxed_type(name = "MyBoxed")]
78 struct MyBoxed(String);
79
80 #[test]
81 fn test_register() {
82 assert!(MyBoxed::static_type().is_valid());
83 }
84
85 #[test]
86 fn test_value() {
87 assert!(MyBoxed::static_type().is_valid());
88
89 let b = MyBoxed(String::from("abc"));
90 let v = b.to_value();
91 let b2 = v.get::<&MyBoxed>().unwrap();
92 assert_eq!(&b, b2);
93 }
94
95 #[test]
96 fn test_from_glib_borrow() {
97 assert!(MyBoxed::static_type().is_valid());
98
99 let b = MyBoxed(String::from("abc"));
100 let raw_ptr = unsafe { MyBoxed::into_glib_ptr(b) };
101
102 // test that the from_glib_borrow does not take ownership of the raw_ptr
103 let _ = unsafe { MyBoxed::from_glib_borrow(raw_ptr) };
104
105 let new_b = unsafe { MyBoxed::from_glib_full(raw_ptr) };
106
107 assert_eq!(new_b.0, "abc".to_string());
108 }
109}
110