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 | |
6 | use 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 |
17 | pub 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 |
34 | pub 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)] |
68 | mod 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 | |