1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Take a look at the license at the top of the repository in the LICENSE file.

use crate::{prelude::*, subclass::prelude::*, translate::*, TypeModule};

pub trait TypeModuleImpl: ObjectImpl + TypeModuleImplExt {
    // rustdoc-stripper-ignore-next
    /// Loads the module, registers one or more object subclasses using
    /// [`register_dynamic_type`] and registers one or more object interfaces
    /// using [`register_dynamic_interface`] (see [`TypeModule`]).
    ///
    /// [`register_dynamic_type`]: ../types/fn.register_dynamic_type.html
    /// [`register_dynamic_interface`]: ../interface/fn.register_dynamic_interface.html
    /// [`TypeModule`]: ../../gobject/auto/type_module/struct.TypeModule.html
    // rustdoc-stripper-ignore-next-stop
    /// loads the module and registers one or more types using
    ///  [`TypeModuleExtManual::register_type()`][crate::prelude::TypeModuleExtManual::register_type()].
    fn load(&self) -> bool;

    // rustdoc-stripper-ignore-next
    /// Unloads the module (see [`TypeModuleExt::unuse`]).
    ///
    /// [`TypeModuleExt::unuse`]: ../../gobject/auto/type_module/trait.TypeModuleExt.html#method.unuse
    // rustdoc-stripper-ignore-next-stop
    /// unloads the module
    fn unload(&self);
}

pub trait TypeModuleImplExt: ObjectSubclass {
    fn parent_load(&self) -> bool;
    fn parent_unload(&self);
}

impl<T: TypeModuleImpl> TypeModuleImplExt for T {
    fn parent_load(&self) -> bool {
        unsafe {
            let data = T::type_data();
            let parent_class = data.as_ref().parent_class() as *const gobject_ffi::GTypeModuleClass;

            let f = (*parent_class)
                .load
                .expect("No parent class implementation for \"load\"");

            from_glib(f(self
                .obj()
                .unsafe_cast_ref::<TypeModule>()
                .to_glib_none()
                .0))
        }
    }

    fn parent_unload(&self) {
        unsafe {
            let data = T::type_data();
            let parent_class = data.as_ref().parent_class() as *const gobject_ffi::GTypeModuleClass;

            let f = (*parent_class)
                .unload
                .expect("No parent class implementation for \"unload\"");

            f(self.obj().unsafe_cast_ref::<TypeModule>().to_glib_none().0);
        }
    }
}

unsafe impl<T: TypeModuleImpl> IsSubclassable<T> for TypeModule {
    fn class_init(class: &mut crate::Class<Self>) {
        Self::parent_class_init::<T>(class);

        let klass = class.as_mut();
        klass.load = Some(load::<T>);
        klass.unload = Some(unload::<T>);
    }
}

unsafe extern "C" fn load<T: TypeModuleImpl>(
    type_module: *mut gobject_ffi::GTypeModule,
) -> ffi::gboolean {
    let instance = &*(type_module as *mut T::Instance);
    let imp = instance.imp();

    let res = imp.load();
    // GLib type system expects a module to never be disposed if types has been
    // successfully loaded.
    // The following code prevents the Rust wrapper (`glib::TypeModule` subclass)
    // to dispose the module when dropped by ensuring the reference count is > 1.
    // Nothing is done if loading types has failed, allowing application to drop
    // and dispose the invalid module.
    if res && (*(type_module as *const gobject_ffi::GObject)).ref_count == 1 {
        unsafe {
            gobject_ffi::g_object_ref(type_module as _);
        }
    }

    res.into_glib()
}

unsafe extern "C" fn unload<T: TypeModuleImpl>(type_module: *mut gobject_ffi::GTypeModule) {
    let instance = &*(type_module as *mut T::Instance);
    let imp = instance.imp();

    imp.unload();
}

#[cfg(test)]
mod tests {
    use crate as glib;

    use super::*;

    mod imp {
        use super::*;

        #[derive(Default)]
        pub struct SimpleModule;

        #[crate::object_subclass]
        impl ObjectSubclass for SimpleModule {
            const NAME: &'static str = "SimpleModule";
            type Type = super::SimpleModule;
            type ParentType = TypeModule;
            type Interfaces = (crate::TypePlugin,);
        }

        impl ObjectImpl for SimpleModule {}

        impl TypePluginImpl for SimpleModule {}

        impl TypeModuleImpl for SimpleModule {
            fn load(&self) -> bool {
                // register types on implementation load
                SimpleModuleType::on_implementation_load(self.obj().upcast_ref::<TypeModule>())
            }

            fn unload(&self) {
                // unregister types on implementation unload
                SimpleModuleType::on_implementation_unload(self.obj().upcast_ref::<TypeModule>());
            }
        }

        #[derive(Default)]
        pub struct SimpleModuleType;

        #[crate::object_subclass]
        #[object_subclass_dynamic]
        impl ObjectSubclass for SimpleModuleType {
            const NAME: &'static str = "SimpleModuleType";
            type Type = super::SimpleModuleType;
        }

        impl ObjectImpl for SimpleModuleType {}
    }

    crate::wrapper! {
        pub struct SimpleModule(ObjectSubclass<imp::SimpleModule>)
        @extends TypeModule, @implements crate::TypePlugin;
    }

    crate::wrapper! {
        pub struct SimpleModuleType(ObjectSubclass<imp::SimpleModuleType>);
    }

    #[test]
    fn test_module() {
        assert!(!imp::SimpleModuleType::type_().is_valid());
        let simple_module = glib::Object::new::<SimpleModule>();
        // simulates the GLib type system to load the module.
        assert!(TypeModuleExt::use_(&simple_module));
        assert!(imp::SimpleModuleType::type_().is_valid());
        TypeModuleExt::unuse(&simple_module);
    }
}