gio/
io_extension_point.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{marker::PhantomData, ptr};
4
5use glib::{translate::*, GString, Type};
6
7use crate::{ffi, IOExtension};
8
9// rustdoc-stripper-ignore-next
10/// Builder for extension points.
11#[derive(Debug)]
12#[must_use = "The builder must be built to be used"]
13pub struct IOExtensionPointBuilder {
14    name: GString,
15    required_type: Option<Type>,
16}
17
18impl IOExtensionPointBuilder {
19    fn new(name: GString) -> Self {
20        Self {
21            name,
22            required_type: None,
23        }
24    }
25
26    #[doc(alias = "g_io_extension_point_set_required_type")]
27    pub fn required_type(self, required_type: Type) -> Self {
28        Self {
29            required_type: Some(required_type),
30            ..self
31        }
32    }
33
34    #[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
35    pub fn build(self) -> IOExtensionPoint {
36        unsafe {
37            let ep = IOExtensionPoint::from_glib_none(ffi::g_io_extension_point_register(
38                self.name.to_glib_none().0,
39            ));
40            if let Some(t) = self.required_type {
41                ffi::g_io_extension_point_set_required_type(ep.0.as_ptr(), t.into_glib());
42            }
43            ep
44        }
45    }
46}
47
48// rustdoc-stripper-ignore-next
49/// An extension point provides a mechanism to extend the functionality of a library or application.
50/// Each extension point is identified by a name, and it may optionally require that any implementation
51/// must be of a certain type.
52#[doc(alias = "GIOExtensionPoint")]
53#[derive(Debug, Copy, Clone, Eq, PartialEq)]
54pub struct IOExtensionPoint(ptr::NonNull<ffi::GIOExtensionPoint>);
55
56impl FromGlibPtrNone<*mut ffi::GIOExtensionPoint> for IOExtensionPoint {
57    #[inline]
58    unsafe fn from_glib_none(ptr: *mut ffi::GIOExtensionPoint) -> Self {
59        debug_assert!(!ptr.is_null());
60        IOExtensionPoint(ptr::NonNull::new_unchecked(ptr))
61    }
62}
63
64impl<'a> ToGlibPtr<'a, *mut ffi::GIOExtensionPoint> for &'a IOExtensionPoint {
65    type Storage = PhantomData<&'a IOExtensionPoint>;
66
67    #[inline]
68    fn to_glib_none(&self) -> Stash<'a, *mut ffi::GIOExtensionPoint, &'a IOExtensionPoint> {
69        Stash(self.0.as_ptr() as *mut ffi::GIOExtensionPoint, PhantomData)
70    }
71}
72
73impl IOExtensionPoint {
74    // rustdoc-stripper-ignore-next
75    /// Create a new builder for an extension point.
76    #[doc(alias = "g_io_extension_point_register")]
77    pub fn builder(name: impl Into<GString>) -> IOExtensionPointBuilder {
78        IOExtensionPointBuilder::new(name.into())
79    }
80
81    #[doc(alias = "g_io_extension_point_lookup")]
82    pub fn lookup(name: impl IntoGStr) -> Option<Self> {
83        name.run_with_gstr(|name| unsafe {
84            let ep = ffi::g_io_extension_point_lookup(name.to_glib_none().0);
85            from_glib_none(ep)
86        })
87    }
88
89    #[doc(alias = "g_io_extension_point_get_extensions")]
90    pub fn extensions(&self) -> Vec<IOExtension> {
91        let mut res = Vec::new();
92        unsafe {
93            let mut l = ffi::g_io_extension_point_get_extensions(self.0.as_ptr());
94            while !l.is_null() {
95                let e: *mut ffi::GIOExtension = Ptr::from((*l).data);
96                res.push(from_glib_none(e));
97                l = (*l).next;
98            }
99        }
100        res
101    }
102
103    #[doc(alias = "g_io_extension_point_get_extension_by_name")]
104    pub fn extension_by_name(&self, name: impl IntoGStr) -> Option<IOExtension> {
105        name.run_with_gstr(|name| unsafe {
106            let e = ffi::g_io_extension_point_get_extension_by_name(
107                self.0.as_ptr(),
108                name.to_glib_none().0,
109            );
110            from_glib_none(e)
111        })
112    }
113
114    #[doc(alias = "g_io_extension_point_get_required_type")]
115    pub fn required_type(&self) -> Type {
116        unsafe { from_glib(ffi::g_io_extension_point_get_required_type(self.0.as_ptr())) }
117    }
118
119    #[doc(alias = "g_io_extension_point_implement")]
120    pub fn implement(
121        extension_point_name: impl IntoGStr,
122        type_: Type,
123        extension_name: impl IntoGStr,
124        priority: i32,
125    ) -> Option<IOExtension> {
126        extension_point_name.run_with_gstr(|extension_point_name| {
127            extension_name.run_with_gstr(|extension_name| unsafe {
128                let e = ffi::g_io_extension_point_implement(
129                    extension_point_name.to_glib_none().0,
130                    type_.into_glib(),
131                    extension_name.to_glib_none().0,
132                    priority,
133                );
134                from_glib_none(e)
135            })
136        })
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use glib::prelude::*;
143
144    use super::*;
145
146    #[test]
147    fn extension_point() {
148        let ep = IOExtensionPoint::lookup("test-extension-point");
149        assert!(ep.is_none());
150
151        let ep = IOExtensionPoint::builder("test-extension-point").build();
152        let ep2 = IOExtensionPoint::lookup("test-extension-point");
153        assert_eq!(ep2, Some(ep));
154
155        let req = ep.required_type();
156        assert_eq!(req, Type::INVALID);
157
158        let ep = IOExtensionPoint::builder("test-extension-point")
159            .required_type(Type::OBJECT)
160            .build();
161        let req = ep.required_type();
162        assert_eq!(req, Type::OBJECT);
163
164        let v = ep.extensions();
165        assert!(v.is_empty());
166
167        let e = IOExtensionPoint::implement(
168            "test-extension-point",
169            <crate::Vfs as StaticType>::static_type(),
170            "extension1",
171            10,
172        );
173        assert!(e.is_some());
174
175        let e = IOExtensionPoint::implement("test-extension-point", Type::OBJECT, "extension2", 20);
176        assert!(e.is_some());
177
178        let v = ep.extensions();
179        assert_eq!(v.len(), 2);
180        assert_eq!(v[0].name(), "extension2");
181        assert_eq!(v[0].type_(), Type::OBJECT);
182        assert_eq!(v[0].priority(), 20);
183        assert_eq!(v[1].name(), "extension1");
184        assert_eq!(v[1].type_(), <crate::Vfs as StaticType>::static_type());
185        assert_eq!(v[1].priority(), 10);
186    }
187}