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::{prelude::*, subclass::prelude::*, translate::*, Error};
6
7use crate::{ffi, Cancellable, Initable};
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    let instance = &*(initable as *mut T::Instance);
57    let imp = instance.imp();
58
59    match imp.init(
60        Option::<Cancellable>::from_glib_borrow(cancellable)
61            .as_ref()
62            .as_ref(),
63    ) {
64        Ok(()) => glib::ffi::GTRUE,
65        Err(e) => {
66            if !error.is_null() {
67                *error = e.into_glib_ptr();
68            }
69            glib::ffi::GFALSE
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::{prelude::*, Cancellable, Initable};
78
79    pub mod imp {
80        use std::cell::Cell;
81
82        use super::*;
83
84        pub struct InitableTestType(pub Cell<u64>);
85
86        #[glib::object_subclass]
87        impl ObjectSubclass for InitableTestType {
88            const NAME: &'static str = "InitableTestType";
89            type Type = super::InitableTestType;
90            type Interfaces = (Initable,);
91
92            fn new() -> Self {
93                Self(Cell::new(0))
94            }
95        }
96
97        impl InitableImpl for InitableTestType {
98            fn init(&self, _cancellable: Option<&Cancellable>) -> Result<(), glib::Error> {
99                self.0.set(0x123456789abcdef);
100                Ok(())
101            }
102        }
103
104        impl ObjectImpl for InitableTestType {}
105    }
106
107    pub mod ffi {
108        use super::*;
109        pub type InitableTestType = <imp::InitableTestType as ObjectSubclass>::Instance;
110
111        pub unsafe extern "C" fn initable_test_type_get_value(this: *mut InitableTestType) -> u64 {
112            let this = super::InitableTestType::from_glib_borrow(this);
113            this.imp().0.get()
114        }
115    }
116
117    glib::wrapper! {
118        pub struct InitableTestType(ObjectSubclass<imp::InitableTestType>)
119            @implements Initable;
120    }
121
122    #[allow(clippy::new_without_default)]
123    impl InitableTestType {
124        pub fn new() -> Self {
125            Initable::new(Option::<&Cancellable>::None)
126                .expect("Failed creation/initialization of InitableTestType object")
127        }
128
129        pub unsafe fn new_uninit() -> Self {
130            // This creates an uninitialized InitableTestType object, for testing
131            // purposes. In real code, using Initable::new (like the new() method
132            // does) is recommended.
133            glib::Object::new_internal(Self::static_type(), &mut [])
134                .downcast()
135                .unwrap()
136        }
137
138        pub fn value(&self) -> u64 {
139            self.imp().0.get()
140        }
141    }
142
143    #[test]
144    fn test_initable_with_init() {
145        let res = unsafe {
146            let test = InitableTestType::new_uninit();
147
148            assert_ne!(0x123456789abcdef, test.value());
149
150            test.init(Option::<&Cancellable>::None).map(|_| test)
151        };
152        assert!(res.is_ok());
153        let test = res.unwrap();
154
155        assert_eq!(0x123456789abcdef, test.value());
156    }
157
158    #[test]
159    fn test_initable_with_initable_new() {
160        let test = InitableTestType::new();
161        assert_eq!(0x123456789abcdef, test.value());
162    }
163
164    #[test]
165    #[should_panic = ""]
166    fn test_initable_new_failure() {
167        let value: u32 = 2;
168        let _ = Initable::builder::<InitableTestType>()
169            .property("invalid-property", value)
170            .build(Option::<&Cancellable>::None);
171        unreachable!();
172    }
173
174    #[test]
175    fn test_initable_with_initable_with_type() {
176        let test = Initable::with_type(
177            InitableTestType::static_type(),
178            Option::<&Cancellable>::None,
179        )
180        .expect("Failed creation/initialization of InitableTestType object from type")
181        .downcast::<InitableTestType>()
182        .expect("Failed downcast of InitableTestType object");
183        assert_eq!(0x123456789abcdef, test.value());
184    }
185
186    #[test]
187    fn test_initable_with_initable_with_values() {
188        let test = Initable::with_type(
189            InitableTestType::static_type(),
190            Option::<&Cancellable>::None,
191        )
192        .expect("Failed creation/initialization of InitableTestType object from values")
193        .downcast::<InitableTestType>()
194        .expect("Failed downcast of InitableTestType object");
195        assert_eq!(0x123456789abcdef, test.value());
196    }
197
198    #[test]
199    fn test_initable_through_ffi() {
200        unsafe {
201            let test = InitableTestType::new_uninit();
202            let test: *mut ffi::InitableTestType = test.as_ptr();
203            let mut error: *mut glib::ffi::GError = std::ptr::null_mut();
204
205            assert_ne!(0x123456789abcdef, ffi::initable_test_type_get_value(test));
206
207            let result = crate::ffi::g_initable_init(
208                test as *mut crate::ffi::GInitable,
209                std::ptr::null_mut(),
210                &mut error,
211            );
212
213            assert_eq!(glib::ffi::GTRUE, result);
214            assert_eq!(error, ptr::null_mut());
215            assert_eq!(0x123456789abcdef, ffi::initable_test_type_get_value(test));
216        }
217    }
218}