gio/subclass/
initable.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::ptr;
4
5use glib::{Error, prelude::*, subclass::prelude::*, translate::*};
6
7use crate::{Cancellable, Initable, ffi};
8
9pub trait InitableImpl: ObjectImpl + ObjectSubclass<Type: IsA<Initable>> {
10    fn init(&self, cancellable: Option<&Cancellable>) -> Result<(), Error> {
11        self.parent_init(cancellable)
12    }
13}
14
15pub trait InitableImplExt: InitableImpl {
16    fn parent_init(&self, cancellable: Option<&Cancellable>) -> Result<(), Error> {
17        unsafe {
18            let type_data = Self::type_data();
19            let parent_iface =
20                type_data.as_ref().parent_interface::<Initable>() as *const ffi::GInitableIface;
21
22            let func = (*parent_iface)
23                .init
24                .expect("no parent \"init\" implementation");
25
26            let mut err = ptr::null_mut();
27            func(
28                self.obj().unsafe_cast_ref::<Initable>().to_glib_none().0,
29                cancellable.to_glib_none().0,
30                &mut err,
31            );
32
33            if err.is_null() {
34                Ok(())
35            } else {
36                Err(from_glib_full(err))
37            }
38        }
39    }
40}
41
42impl<T: InitableImpl> InitableImplExt for T {}
43
44unsafe impl<T: InitableImpl> IsImplementable<T> for Initable {
45    fn interface_init(iface: &mut glib::Interface<Self>) {
46        let iface = iface.as_mut();
47        iface.init = Some(initable_init::<T>);
48    }
49}
50
51unsafe extern "C" fn initable_init<T: InitableImpl>(
52    initable: *mut ffi::GInitable,
53    cancellable: *mut ffi::GCancellable,
54    error: *mut *mut glib::ffi::GError,
55) -> glib::ffi::gboolean {
56    unsafe {
57        let instance = &*(initable as *mut T::Instance);
58        let imp = instance.imp();
59
60        match imp.init(
61            Option::<Cancellable>::from_glib_borrow(cancellable)
62                .as_ref()
63                .as_ref(),
64        ) {
65            Ok(()) => glib::ffi::GTRUE,
66            Err(e) => {
67                if !error.is_null() {
68                    *error = e.into_glib_ptr();
69                }
70                glib::ffi::GFALSE
71            }
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::{Cancellable, Initable, prelude::*};
80
81    pub mod imp {
82        use std::cell::Cell;
83
84        use super::*;
85
86        pub struct InitableTestType(pub Cell<u64>);
87
88        #[glib::object_subclass]
89        impl ObjectSubclass for InitableTestType {
90            const NAME: &'static str = "InitableTestType";
91            type Type = super::InitableTestType;
92            type Interfaces = (Initable,);
93
94            fn new() -> Self {
95                Self(Cell::new(0))
96            }
97        }
98
99        impl InitableImpl for InitableTestType {
100            fn init(&self, _cancellable: Option<&Cancellable>) -> Result<(), glib::Error> {
101                self.0.set(0x123456789abcdef);
102                Ok(())
103            }
104        }
105
106        impl ObjectImpl for InitableTestType {}
107    }
108
109    pub mod ffi {
110        use super::*;
111        pub type InitableTestType = <imp::InitableTestType as ObjectSubclass>::Instance;
112
113        pub unsafe extern "C" fn initable_test_type_get_value(this: *mut InitableTestType) -> u64 {
114            unsafe {
115                let this = super::InitableTestType::from_glib_borrow(this);
116                this.imp().0.get()
117            }
118        }
119    }
120
121    glib::wrapper! {
122        pub struct InitableTestType(ObjectSubclass<imp::InitableTestType>)
123            @implements Initable;
124    }
125
126    #[allow(clippy::new_without_default)]
127    impl InitableTestType {
128        pub fn new() -> Self {
129            Initable::new(Option::<&Cancellable>::None)
130                .expect("Failed creation/initialization of InitableTestType object")
131        }
132
133        pub unsafe fn new_uninit() -> Self {
134            unsafe {
135                // This creates an uninitialized InitableTestType object, for testing
136                // purposes. In real code, using Initable::new (like the new() method
137                // does) is recommended.
138                glib::Object::new_internal(Self::static_type(), &mut [])
139                    .downcast()
140                    .unwrap()
141            }
142        }
143
144        pub fn value(&self) -> u64 {
145            self.imp().0.get()
146        }
147    }
148
149    #[test]
150    fn test_initable_with_init() {
151        let res = unsafe {
152            let test = InitableTestType::new_uninit();
153
154            assert_ne!(0x123456789abcdef, test.value());
155
156            test.init(Option::<&Cancellable>::None).map(|_| test)
157        };
158        assert!(res.is_ok());
159        let test = res.unwrap();
160
161        assert_eq!(0x123456789abcdef, test.value());
162    }
163
164    #[test]
165    fn test_initable_with_initable_new() {
166        let test = InitableTestType::new();
167        assert_eq!(0x123456789abcdef, test.value());
168    }
169
170    #[test]
171    #[should_panic = ""]
172    fn test_initable_new_failure() {
173        let value: u32 = 2;
174        let _ = Initable::builder::<InitableTestType>()
175            .property("invalid-property", value)
176            .build(Option::<&Cancellable>::None);
177        unreachable!();
178    }
179
180    #[test]
181    fn test_initable_with_initable_with_type() {
182        let test = Initable::with_type(
183            InitableTestType::static_type(),
184            Option::<&Cancellable>::None,
185        )
186        .expect("Failed creation/initialization of InitableTestType object from type")
187        .downcast::<InitableTestType>()
188        .expect("Failed downcast of InitableTestType object");
189        assert_eq!(0x123456789abcdef, test.value());
190    }
191
192    #[test]
193    fn test_initable_with_initable_with_values() {
194        let test = Initable::with_type(
195            InitableTestType::static_type(),
196            Option::<&Cancellable>::None,
197        )
198        .expect("Failed creation/initialization of InitableTestType object from values")
199        .downcast::<InitableTestType>()
200        .expect("Failed downcast of InitableTestType object");
201        assert_eq!(0x123456789abcdef, test.value());
202    }
203
204    #[test]
205    fn test_initable_through_ffi() {
206        unsafe {
207            let test = InitableTestType::new_uninit();
208            let test: *mut ffi::InitableTestType = test.as_ptr();
209            let mut error: *mut glib::ffi::GError = std::ptr::null_mut();
210
211            assert_ne!(0x123456789abcdef, ffi::initable_test_type_get_value(test));
212
213            let result = crate::ffi::g_initable_init(
214                test as *mut crate::ffi::GInitable,
215                std::ptr::null_mut(),
216                &mut error,
217            );
218
219            assert_eq!(glib::ffi::GTRUE, result);
220            assert_eq!(error, ptr::null_mut());
221            assert_eq!(0x123456789abcdef, ffi::initable_test_type_get_value(test));
222        }
223    }
224}