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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{marker::PhantomData, ptr};
use glib::{translate::*, GString, Type};
use crate::IOExtension;
// rustdoc-stripper-ignore-next
/// Builder for extension points.
#[derive(Debug)]
#[must_use = "The builder must be built to be used"]
pub struct IOExtensionPointBuilder {
name: GString,
required_type: Option<Type>,
}
impl IOExtensionPointBuilder {
fn new(name: GString) -> Self {
Self {
name,
required_type: None,
}
}
#[doc(alias = "g_io_extension_point_set_required_type")]
pub fn required_type(self, required_type: Type) -> Self {
Self {
required_type: Some(required_type),
..self
}
}
#[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
pub fn build(self) -> IOExtensionPoint {
unsafe {
let ep = IOExtensionPoint::from_glib_none(ffi::g_io_extension_point_register(
self.name.to_glib_none().0,
));
if let Some(t) = self.required_type {
ffi::g_io_extension_point_set_required_type(ep.0.as_ptr(), t.into_glib());
}
ep
}
}
}
// rustdoc-stripper-ignore-next
/// An extension point provides a mechanism to extend the functionality of a library or application.
/// Each extension point is identified by a name, and it may optionally require that any implementation
/// must be of a certain type.
// rustdoc-stripper-ignore-next-stop
/// `GIOExtensionPoint` provides a mechanism for modules to extend the
/// functionality of the library or application that loaded it in an
/// organized fashion.
///
/// An extension point is identified by a name, and it may optionally
/// require that any implementation must be of a certain type (or derived
/// thereof). Use [`register()`][Self::register()] to register an
/// extension point, and [`set_required_type()`][Self::set_required_type()] to
/// set a required type.
///
/// A module can implement an extension point by specifying the
/// [type@GObject.Type] that implements the functionality. Additionally, each
/// implementation of an extension point has a name, and a priority. Use
/// [`implement()`][Self::implement()] to implement an extension point.
///
/// **⚠️ The following code is in c ⚠️**
///
/// ```c
/// GIOExtensionPoint *ep;
///
/// // Register an extension point
/// ep = g_io_extension_point_register ("my-extension-point");
/// g_io_extension_point_set_required_type (ep, MY_TYPE_EXAMPLE);
/// ```
///
/// **⚠️ The following code is in c ⚠️**
///
/// ```c
/// // Implement an extension point
/// G_DEFINE_TYPE (MyExampleImpl, my_example_impl, MY_TYPE_EXAMPLE)
/// g_io_extension_point_implement ("my-extension-point",
/// my_example_impl_get_type (),
/// "my-example",
/// 10);
/// ```
///
/// It is up to the code that registered the extension point how
/// it uses the implementations that have been associated with it.
/// Depending on the use case, it may use all implementations, or
/// only the one with the highest priority, or pick a specific
/// one by name.
///
/// To avoid opening all modules just to find out what extension
/// points they implement, GIO makes use of a caching mechanism,
/// see [gio-querymodules](gio-querymodules.html).
/// You are expected to run this command after installing a
/// GIO module.
///
/// The `GIO_EXTRA_MODULES` environment variable can be used to
/// specify additional directories to automatically load modules
/// from. This environment variable has the same syntax as the
/// `PATH`. If two modules have the same base name in different
/// directories, then the latter one will be ignored. If additional
/// directories are specified GIO will load modules from the built-in
/// directory last.
#[doc(alias = "GIOExtensionPoint")]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct IOExtensionPoint(ptr::NonNull<ffi::GIOExtensionPoint>);
impl FromGlibPtrNone<*mut ffi::GIOExtensionPoint> for IOExtensionPoint {
#[inline]
unsafe fn from_glib_none(ptr: *mut ffi::GIOExtensionPoint) -> Self {
debug_assert!(!ptr.is_null());
IOExtensionPoint(ptr::NonNull::new_unchecked(ptr))
}
}
impl<'a> ToGlibPtr<'a, *mut ffi::GIOExtensionPoint> for &'a IOExtensionPoint {
type Storage = PhantomData<&'a IOExtensionPoint>;
#[inline]
fn to_glib_none(&self) -> Stash<'a, *mut ffi::GIOExtensionPoint, &'a IOExtensionPoint> {
Stash(self.0.as_ptr() as *mut ffi::GIOExtensionPoint, PhantomData)
}
}
impl IOExtensionPoint {
// rustdoc-stripper-ignore-next
/// Create a new builder for an extension point.
#[doc(alias = "g_io_extension_point_register")]
pub fn builder(name: impl Into<GString>) -> IOExtensionPointBuilder {
IOExtensionPointBuilder::new(name.into())
}
/// Looks up an existing extension point.
/// ## `name`
/// the name of the extension point
///
/// # Returns
///
/// the #GIOExtensionPoint, or [`None`] if there
/// is no registered extension point with the given name.
#[doc(alias = "g_io_extension_point_lookup")]
pub fn lookup(name: impl IntoGStr) -> Option<Self> {
name.run_with_gstr(|name| unsafe {
let ep = ffi::g_io_extension_point_lookup(name.to_glib_none().0);
from_glib_none(ep)
})
}
/// Gets a list of all extensions that implement this extension point.
/// The list is sorted by priority, beginning with the highest priority.
///
/// # Returns
///
/// a #GList of
/// #GIOExtensions. The list is owned by GIO and should not be
/// modified.
#[doc(alias = "g_io_extension_point_get_extensions")]
pub fn extensions(&self) -> Vec<IOExtension> {
let mut res = Vec::new();
unsafe {
let mut l = ffi::g_io_extension_point_get_extensions(self.0.as_ptr());
while !l.is_null() {
let e: *mut ffi::GIOExtension = Ptr::from((*l).data);
res.push(from_glib_none(e));
l = (*l).next;
}
}
res
}
/// Finds a #GIOExtension for an extension point by name.
/// ## `name`
/// the name of the extension to get
///
/// # Returns
///
/// the #GIOExtension for @self that has the
/// given name, or [`None`] if there is no extension with that name
#[doc(alias = "g_io_extension_point_get_extension_by_name")]
pub fn extension_by_name(&self, name: impl IntoGStr) -> Option<IOExtension> {
name.run_with_gstr(|name| unsafe {
let e = ffi::g_io_extension_point_get_extension_by_name(
self.0.as_ptr(),
name.to_glib_none().0,
);
from_glib_none(e)
})
}
/// Gets the required type for @self.
///
/// # Returns
///
/// the #GType that all implementations must have,
/// or `G_TYPE_INVALID` if the extension point has no required type
#[doc(alias = "g_io_extension_point_get_required_type")]
pub fn required_type(&self) -> Type {
unsafe { from_glib(ffi::g_io_extension_point_get_required_type(self.0.as_ptr())) }
}
/// Registers @type_ as extension for the extension point with name
/// @extension_point_name.
///
/// If @type_ has already been registered as an extension for this
/// extension point, the existing #GIOExtension object is returned.
/// ## `extension_point_name`
/// the name of the extension point
/// ## `type_`
/// the #GType to register as extension
/// ## `extension_name`
/// the name for the extension
/// ## `priority`
/// the priority for the extension
///
/// # Returns
///
/// a #GIOExtension object for #GType
#[doc(alias = "g_io_extension_point_implement")]
pub fn implement(
extension_point_name: impl IntoGStr,
type_: Type,
extension_name: impl IntoGStr,
priority: i32,
) -> Option<IOExtension> {
extension_point_name.run_with_gstr(|extension_point_name| {
extension_name.run_with_gstr(|extension_name| unsafe {
let e = ffi::g_io_extension_point_implement(
extension_point_name.to_glib_none().0,
type_.into_glib(),
extension_name.to_glib_none().0,
priority,
);
from_glib_none(e)
})
})
}
}
#[cfg(test)]
mod tests {
use glib::prelude::*;
use super::*;
#[test]
fn extension_point() {
let ep = IOExtensionPoint::lookup("test-extension-point");
assert!(ep.is_none());
let ep = IOExtensionPoint::builder("test-extension-point").build();
let ep2 = IOExtensionPoint::lookup("test-extension-point");
assert_eq!(ep2, Some(ep));
let req = ep.required_type();
assert_eq!(req, Type::INVALID);
let ep = IOExtensionPoint::builder("test-extension-point")
.required_type(Type::OBJECT)
.build();
let req = ep.required_type();
assert_eq!(req, Type::OBJECT);
let v = ep.extensions();
assert!(v.is_empty());
let e = IOExtensionPoint::implement(
"test-extension-point",
<crate::Vfs as StaticType>::static_type(),
"extension1",
10,
);
assert!(e.is_some());
let e = IOExtensionPoint::implement("test-extension-point", Type::OBJECT, "extension2", 20);
assert!(e.is_some());
let v = ep.extensions();
assert_eq!(v.len(), 2);
assert_eq!(v[0].name(), "extension2");
assert_eq!(v[0].type_(), Type::OBJECT);
assert_eq!(v[0].priority(), 20);
assert_eq!(v[1].name(), "extension1");
assert_eq!(v[1].type_(), <crate::Vfs as StaticType>::static_type());
assert_eq!(v[1].priority(), 10);
}
}