glib/
types.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Runtime type information.
5
6use std::{
7    fmt,
8    marker::PhantomData,
9    mem,
10    num::{NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU32, NonZeroU64, NonZeroU8},
11    path::{Path, PathBuf},
12    ptr,
13};
14
15use crate::{ffi, gobject_ffi, prelude::*, translate::*, Slice, TypeFlags, TypePlugin};
16
17// rustdoc-stripper-ignore-next
18/// A GLib or GLib-based library type
19#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[doc(alias = "GType")]
21#[repr(transparent)]
22pub struct Type(ffi::GType);
23
24unsafe impl TransparentType for Type {
25    type GlibType = ffi::GType;
26}
27
28impl Type {
29    // rustdoc-stripper-ignore-next
30    /// An invalid `Type` used as error return value in some functions
31    #[doc(alias = "G_TYPE_INVALID")]
32    pub const INVALID: Self = Self(gobject_ffi::G_TYPE_INVALID);
33
34    // rustdoc-stripper-ignore-next
35    /// The fundamental type corresponding to the unit type `()`
36    #[doc(alias = "G_TYPE_NONE")]
37    pub const UNIT: Self = Self(gobject_ffi::G_TYPE_NONE);
38
39    // rustdoc-stripper-ignore-next
40    /// The fundamental type corresponding to `i8`
41    #[doc(alias = "G_TYPE_CHAR")]
42    pub const I8: Self = Self(gobject_ffi::G_TYPE_CHAR);
43
44    // rustdoc-stripper-ignore-next
45    /// The fundamental type corresponding to `u8`
46    #[doc(alias = "G_TYPE_UCHAR")]
47    pub const U8: Self = Self(gobject_ffi::G_TYPE_UCHAR);
48
49    // rustdoc-stripper-ignore-next
50    /// The fundamental type corresponding to `bool`
51    #[doc(alias = "G_TYPE_BOOLEAN")]
52    pub const BOOL: Self = Self(gobject_ffi::G_TYPE_BOOLEAN);
53
54    // rustdoc-stripper-ignore-next
55    /// The fundamental type corresponding to `i32`
56    #[doc(alias = "G_TYPE_INT")]
57    pub const I32: Self = Self(gobject_ffi::G_TYPE_INT);
58
59    // rustdoc-stripper-ignore-next
60    /// The fundamental type corresponding to `u32`
61    #[doc(alias = "G_TYPE_UINT")]
62    pub const U32: Self = Self(gobject_ffi::G_TYPE_UINT);
63
64    // rustdoc-stripper-ignore-next
65    /// The fundamental type corresponding to C `long`
66    #[doc(alias = "G_TYPE_LONG")]
67    pub const I_LONG: Self = Self(gobject_ffi::G_TYPE_LONG);
68
69    // rustdoc-stripper-ignore-next
70    /// The fundamental type corresponding to C `unsigned long`
71    #[doc(alias = "G_TYPE_ULONG")]
72    pub const U_LONG: Self = Self(gobject_ffi::G_TYPE_ULONG);
73
74    // rustdoc-stripper-ignore-next
75    /// The fundamental type corresponding to `i64`
76    #[doc(alias = "G_TYPE_INT64")]
77    pub const I64: Self = Self(gobject_ffi::G_TYPE_INT64);
78
79    // rustdoc-stripper-ignore-next
80    /// The fundamental type corresponding to `u64`
81    #[doc(alias = "G_TYPE_UINT64")]
82    pub const U64: Self = Self(gobject_ffi::G_TYPE_UINT64);
83
84    // rustdoc-stripper-ignore-next
85    /// The fundamental type corresponding to `f32`
86    #[doc(alias = "G_TYPE_FLOAT")]
87    pub const F32: Self = Self(gobject_ffi::G_TYPE_FLOAT);
88
89    // rustdoc-stripper-ignore-next
90    /// The fundamental type corresponding to `f64`
91    #[doc(alias = "G_TYPE_DOUBLE")]
92    pub const F64: Self = Self(gobject_ffi::G_TYPE_DOUBLE);
93
94    // rustdoc-stripper-ignore-next
95    /// The fundamental type corresponding to `String`
96    #[doc(alias = "G_TYPE_STRING")]
97    pub const STRING: Self = Self(gobject_ffi::G_TYPE_STRING);
98
99    // rustdoc-stripper-ignore-next
100    /// The fundamental type corresponding to a pointer
101    #[doc(alias = "G_TYPE_POINTER")]
102    pub const POINTER: Self = Self(gobject_ffi::G_TYPE_POINTER);
103
104    // rustdoc-stripper-ignore-next
105    /// The fundamental type of GVariant
106    #[doc(alias = "G_TYPE_VARIANT")]
107    pub const VARIANT: Self = Self(gobject_ffi::G_TYPE_VARIANT);
108
109    // rustdoc-stripper-ignore-next
110    /// The fundamental type from which all interfaces are derived
111    #[doc(alias = "G_TYPE_INTERFACE")]
112    pub const INTERFACE: Self = Self(gobject_ffi::G_TYPE_INTERFACE);
113
114    // rustdoc-stripper-ignore-next
115    /// The fundamental type from which all enumeration types are derived
116    #[doc(alias = "G_TYPE_ENUM")]
117    pub const ENUM: Self = Self(gobject_ffi::G_TYPE_ENUM);
118
119    // rustdoc-stripper-ignore-next
120    /// The fundamental type from which all flags types are derived
121    #[doc(alias = "G_TYPE_FLAGS")]
122    pub const FLAGS: Self = Self(gobject_ffi::G_TYPE_FLAGS);
123
124    // rustdoc-stripper-ignore-next
125    /// The fundamental type from which all boxed types are derived
126    #[doc(alias = "G_TYPE_BOXED")]
127    pub const BOXED: Self = Self(gobject_ffi::G_TYPE_BOXED);
128
129    // rustdoc-stripper-ignore-next
130    /// The fundamental type from which all `GParamSpec` types are derived
131    #[doc(alias = "G_TYPE_PARAM")]
132    pub const PARAM_SPEC: Self = Self(gobject_ffi::G_TYPE_PARAM);
133
134    // rustdoc-stripper-ignore-next
135    /// The fundamental type from which all objects are derived
136    #[doc(alias = "G_TYPE_OBJECT")]
137    pub const OBJECT: Self = Self(gobject_ffi::G_TYPE_OBJECT);
138
139    #[doc(alias = "g_type_name")]
140    pub fn name<'a>(self) -> &'a str {
141        match self.into_glib() {
142            gobject_ffi::G_TYPE_INVALID => "<invalid>",
143            x => unsafe {
144                let ptr = gobject_ffi::g_type_name(x);
145                std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
146            },
147        }
148    }
149
150    #[doc(alias = "g_type_qname")]
151    pub fn qname(self) -> crate::Quark {
152        match self.into_glib() {
153            gobject_ffi::G_TYPE_INVALID => crate::Quark::from_str("<invalid>"),
154            x => unsafe { from_glib(gobject_ffi::g_type_qname(x)) },
155        }
156    }
157
158    #[doc(alias = "g_type_is_a")]
159    #[inline]
160    pub fn is_a(self, other: Self) -> bool {
161        unsafe {
162            from_glib(gobject_ffi::g_type_is_a(
163                self.into_glib(),
164                other.into_glib(),
165            ))
166        }
167    }
168
169    #[doc(alias = "g_type_parent")]
170    pub fn parent(self) -> Option<Self> {
171        unsafe {
172            let parent: Self = from_glib(gobject_ffi::g_type_parent(self.into_glib()));
173            Some(parent).filter(|t| t.is_valid())
174        }
175    }
176
177    #[doc(alias = "g_type_children")]
178    pub fn children(self) -> Slice<Self> {
179        unsafe {
180            let mut n_children = 0u32;
181            let children = gobject_ffi::g_type_children(self.into_glib(), &mut n_children);
182
183            Slice::from_glib_full_num(children, n_children as usize)
184        }
185    }
186
187    #[doc(alias = "g_type_interfaces")]
188    pub fn interfaces(self) -> Slice<Self> {
189        unsafe {
190            let mut n_interfaces = 0u32;
191            let interfaces = gobject_ffi::g_type_interfaces(self.into_glib(), &mut n_interfaces);
192
193            Slice::from_glib_full_num(interfaces, n_interfaces as usize)
194        }
195    }
196
197    #[doc(alias = "g_type_interface_prerequisites")]
198    pub fn interface_prerequisites(self) -> Slice<Self> {
199        unsafe {
200            match self {
201                t if !t.is_a(Self::INTERFACE) => Slice::from_glib_full_num(ptr::null_mut(), 0),
202                _ => {
203                    let mut n_prereqs = 0u32;
204                    let prereqs = gobject_ffi::g_type_interface_prerequisites(
205                        self.into_glib(),
206                        &mut n_prereqs,
207                    );
208
209                    Slice::from_glib_full_num(prereqs, n_prereqs as usize)
210                }
211            }
212        }
213    }
214
215    #[doc(alias = "g_type_from_name")]
216    pub fn from_name(name: impl IntoGStr) -> Option<Self> {
217        unsafe {
218            let type_ = name.run_with_gstr(|name| {
219                Self::from_glib(gobject_ffi::g_type_from_name(name.as_ptr()))
220            });
221
222            Some(type_).filter(|t| t.is_valid())
223        }
224    }
225
226    #[doc(alias = "g_type_get_plugin")]
227    pub fn plugin(self) -> Option<TypePlugin> {
228        unsafe {
229            let plugin_ptr = gobject_ffi::g_type_get_plugin(self.into_glib());
230            if plugin_ptr.is_null() {
231                None
232            } else {
233                Some(TypePlugin::from_glib_none(plugin_ptr))
234            }
235        }
236    }
237
238    #[doc(alias = "g_type_register_dynamic")]
239    pub fn register_dynamic(
240        parent_type: Self,
241        name: impl IntoGStr,
242        plugin: &TypePlugin,
243        flags: TypeFlags,
244    ) -> Self {
245        unsafe {
246            name.run_with_gstr(|name| {
247                Self::from_glib(gobject_ffi::g_type_register_dynamic(
248                    parent_type.into_glib(),
249                    name.as_ptr(),
250                    plugin.as_ptr(),
251                    flags.into_glib(),
252                ))
253            })
254        }
255    }
256
257    #[doc(alias = "g_type_add_interface_dynamic")]
258    pub fn add_interface_dynamic(self, interface_type: Self, plugin: &TypePlugin) {
259        unsafe {
260            gobject_ffi::g_type_add_interface_dynamic(
261                self.into_glib(),
262                interface_type.into_glib(),
263                plugin.as_ptr(),
264            );
265        }
266    }
267
268    // rustdoc-stripper-ignore-next
269    /// Checks that the type is not [`INVALID`](Self::INVALID)
270    #[inline]
271    pub fn is_valid(self) -> bool {
272        self != Self::INVALID
273    }
274}
275
276impl fmt::Debug for Type {
277    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
278        f.write_str(self.name())
279    }
280}
281
282impl fmt::Display for Type {
283    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284        f.write_str(self.name())
285    }
286}
287
288// rustdoc-stripper-ignore-next
289/// Types that are supported by GLib dynamic typing.
290pub trait StaticType {
291    // rustdoc-stripper-ignore-next
292    /// Returns the type identifier of `Self`.
293    fn static_type() -> Type;
294}
295
296impl StaticType for Type {
297    #[doc(alias = "g_gtype_get_type")]
298    #[inline]
299    fn static_type() -> Type {
300        unsafe { from_glib(gobject_ffi::g_gtype_get_type()) }
301    }
302}
303
304pub trait StaticTypeExt {
305    // rustdoc-stripper-ignore-next
306    /// Ensures that the type has been registered with the type system.
307    #[doc(alias = "g_type_ensure")]
308    fn ensure_type();
309}
310
311impl<T: StaticType> StaticTypeExt for T {
312    #[inline]
313    fn ensure_type() {
314        T::static_type();
315    }
316}
317
318#[doc(hidden)]
319impl crate::value::ValueType for Type {
320    type Type = Type;
321}
322
323#[doc(hidden)]
324unsafe impl<'a> crate::value::FromValue<'a> for Type {
325    type Checker = crate::value::GenericValueTypeChecker<Self>;
326
327    #[inline]
328    unsafe fn from_value(value: &'a crate::Value) -> Self {
329        from_glib(gobject_ffi::g_value_get_gtype(value.to_glib_none().0))
330    }
331}
332
333#[doc(hidden)]
334impl crate::value::ToValue for Type {
335    #[inline]
336    fn to_value(&self) -> crate::Value {
337        unsafe {
338            let mut value = crate::Value::from_type_unchecked(Type::static_type());
339            gobject_ffi::g_value_set_gtype(value.to_glib_none_mut().0, self.into_glib());
340            value
341        }
342    }
343
344    #[inline]
345    fn value_type(&self) -> crate::Type {
346        Type::static_type()
347    }
348}
349
350#[doc(hidden)]
351impl From<Type> for crate::Value {
352    #[inline]
353    fn from(t: Type) -> Self {
354        crate::value::ToValue::to_value(&t)
355    }
356}
357
358impl<T: ?Sized + StaticType> StaticType for &'_ T {
359    #[inline]
360    fn static_type() -> Type {
361        T::static_type()
362    }
363}
364
365impl<T: ?Sized + StaticType> StaticType for &'_ mut T {
366    #[inline]
367    fn static_type() -> Type {
368        T::static_type()
369    }
370}
371
372macro_rules! builtin {
373    ($name:ty, $val:ident) => {
374        impl StaticType for $name {
375            #[inline]
376            fn static_type() -> Type {
377                Type::$val
378            }
379        }
380    };
381}
382
383// rustdoc-stripper-ignore-next
384/// A GLib pointer
385///
386/// A raw untyped pointer equivalent to [`*mut Pointee`](Pointee).
387pub type Pointer = ffi::gpointer;
388
389// rustdoc-stripper-ignore-next
390/// The target of a [Pointer]
391///
392/// # Examples
393///
394/// ```
395/// use glib::prelude::*;
396/// use glib::types::{Pointee, Pointer};
397/// use std::ptr::NonNull;
398///
399/// let pointer = NonNull::<Pointee>::dangling();
400/// let value = pointer.to_value();
401/// assert!(value.is::<Pointer>());
402/// assert_eq!(value.get(), Ok(pointer.as_ptr()));
403/// ```
404pub type Pointee = libc::c_void;
405
406impl StaticType for ptr::NonNull<Pointee> {
407    #[inline]
408    fn static_type() -> Type {
409        Pointer::static_type()
410    }
411}
412
413#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
414pub struct ILong(pub libc::c_long);
415
416impl std::ops::Deref for ILong {
417    type Target = libc::c_long;
418
419    #[inline]
420    fn deref(&self) -> &Self::Target {
421        &self.0
422    }
423}
424
425impl std::ops::DerefMut for ILong {
426    #[inline]
427    fn deref_mut(&mut self) -> &mut Self::Target {
428        &mut self.0
429    }
430}
431
432impl From<libc::c_long> for ILong {
433    #[inline]
434    fn from(v: libc::c_long) -> ILong {
435        ILong(v)
436    }
437}
438
439impl From<ILong> for libc::c_long {
440    #[inline]
441    fn from(v: ILong) -> libc::c_long {
442        v.0
443    }
444}
445
446impl PartialEq<libc::c_long> for ILong {
447    #[inline]
448    fn eq(&self, other: &libc::c_long) -> bool {
449        &self.0 == other
450    }
451}
452
453impl PartialEq<ILong> for libc::c_long {
454    #[inline]
455    fn eq(&self, other: &ILong) -> bool {
456        self == &other.0
457    }
458}
459
460impl PartialOrd<libc::c_long> for ILong {
461    #[inline]
462    fn partial_cmp(&self, other: &libc::c_long) -> Option<std::cmp::Ordering> {
463        self.0.partial_cmp(other)
464    }
465}
466
467impl PartialOrd<ILong> for libc::c_long {
468    #[inline]
469    fn partial_cmp(&self, other: &ILong) -> Option<std::cmp::Ordering> {
470        self.partial_cmp(&other.0)
471    }
472}
473
474#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
475pub struct ULong(pub libc::c_ulong);
476
477impl std::ops::Deref for ULong {
478    type Target = libc::c_ulong;
479
480    #[inline]
481    fn deref(&self) -> &Self::Target {
482        &self.0
483    }
484}
485
486impl std::ops::DerefMut for ULong {
487    #[inline]
488    fn deref_mut(&mut self) -> &mut Self::Target {
489        &mut self.0
490    }
491}
492
493impl From<libc::c_ulong> for ULong {
494    #[inline]
495    fn from(v: libc::c_ulong) -> ULong {
496        ULong(v)
497    }
498}
499
500impl From<ULong> for libc::c_ulong {
501    #[inline]
502    fn from(v: ULong) -> libc::c_ulong {
503        v.0
504    }
505}
506
507impl PartialEq<libc::c_ulong> for ULong {
508    #[inline]
509    fn eq(&self, other: &libc::c_ulong) -> bool {
510        &self.0 == other
511    }
512}
513
514impl PartialEq<ULong> for libc::c_ulong {
515    #[inline]
516    fn eq(&self, other: &ULong) -> bool {
517        self == &other.0
518    }
519}
520
521impl PartialOrd<libc::c_ulong> for ULong {
522    #[inline]
523    fn partial_cmp(&self, other: &libc::c_ulong) -> Option<std::cmp::Ordering> {
524        self.0.partial_cmp(other)
525    }
526}
527
528impl PartialOrd<ULong> for libc::c_ulong {
529    #[inline]
530    fn partial_cmp(&self, other: &ULong) -> Option<std::cmp::Ordering> {
531        self.partial_cmp(&other.0)
532    }
533}
534
535builtin!(bool, BOOL);
536builtin!(i8, I8);
537builtin!(NonZeroI8, I8);
538builtin!(u8, U8);
539builtin!(NonZeroU8, U8);
540builtin!(i32, I32);
541builtin!(NonZeroI32, I32);
542builtin!(u32, U32);
543builtin!(NonZeroU32, U32);
544builtin!(i64, I64);
545builtin!(NonZeroI64, I64);
546builtin!(u64, U64);
547builtin!(NonZeroU64, U64);
548builtin!(ILong, I_LONG);
549builtin!(ULong, U_LONG);
550builtin!(f32, F32);
551builtin!(f64, F64);
552builtin!(str, STRING);
553builtin!(String, STRING);
554builtin!(PathBuf, STRING);
555builtin!(Path, STRING);
556builtin!(Pointer, POINTER);
557
558impl StaticType for [&'_ str] {
559    #[inline]
560    fn static_type() -> Type {
561        unsafe { from_glib(ffi::g_strv_get_type()) }
562    }
563}
564
565impl StaticType for Vec<String> {
566    #[inline]
567    fn static_type() -> Type {
568        unsafe { from_glib(ffi::g_strv_get_type()) }
569    }
570}
571
572impl StaticType for () {
573    #[inline]
574    fn static_type() -> Type {
575        Type::UNIT
576    }
577}
578
579#[inline]
580pub unsafe fn instance_of<C: StaticType>(ptr: ffi::gconstpointer) -> bool {
581    from_glib(gobject_ffi::g_type_check_instance_is_a(
582        ptr as *mut _,
583        <C as StaticType>::static_type().into_glib(),
584    ))
585}
586
587impl FromGlib<ffi::GType> for Type {
588    #[inline]
589    unsafe fn from_glib(val: ffi::GType) -> Self {
590        Self(val)
591    }
592}
593
594impl IntoGlib for Type {
595    type GlibType = ffi::GType;
596
597    #[inline]
598    fn into_glib(self) -> ffi::GType {
599        self.0
600    }
601}
602
603impl<'a> ToGlibContainerFromSlice<'a, *mut ffi::GType> for Type {
604    type Storage = PhantomData<&'a [Type]>;
605
606    #[inline]
607    fn to_glib_none_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
608        (t.as_ptr() as *mut ffi::GType, PhantomData)
609    }
610
611    #[inline]
612    fn to_glib_container_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
613        (Self::to_glib_full_from_slice(t), PhantomData)
614    }
615
616    fn to_glib_full_from_slice(t: &[Type]) -> *mut ffi::GType {
617        if t.is_empty() {
618            return ptr::null_mut();
619        }
620
621        unsafe {
622            let res =
623                ffi::g_malloc(mem::size_of::<ffi::GType>() * (t.len() + 1)) as *mut ffi::GType;
624            std::ptr::copy_nonoverlapping(t.as_ptr() as *const ffi::GType, res, t.len());
625            *res.add(t.len()) = 0;
626            res
627        }
628    }
629}
630
631impl FromGlibContainerAsVec<Type, *const ffi::GType> for Type {
632    unsafe fn from_glib_none_num_as_vec(ptr: *const ffi::GType, num: usize) -> Vec<Self> {
633        if num == 0 || ptr.is_null() {
634            return Vec::new();
635        }
636
637        let mut res = Vec::<Self>::with_capacity(num);
638        let res_ptr = res.as_mut_ptr() as *mut ffi::GType;
639        std::ptr::copy_nonoverlapping(ptr, res_ptr, num);
640        res.set_len(num);
641        res
642    }
643
644    unsafe fn from_glib_container_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
645        // Can't really free a *const
646        unimplemented!();
647    }
648
649    unsafe fn from_glib_full_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
650        // Can't really free a *const
651        unimplemented!();
652    }
653}
654
655impl FromGlibContainerAsVec<Type, *mut ffi::GType> for Type {
656    unsafe fn from_glib_none_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
657        FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *const _, num)
658    }
659
660    unsafe fn from_glib_container_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
661        let res = FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
662        ffi::g_free(ptr as *mut _);
663        res
664    }
665
666    unsafe fn from_glib_full_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
667        FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, num)
668    }
669}
670
671#[cfg(test)]
672mod tests {
673    use std::collections::{BTreeSet, HashSet};
674
675    use super::*;
676    use crate::InitiallyUnowned;
677
678    #[test]
679    fn invalid() {
680        let invalid = Type::INVALID;
681
682        assert_eq!(invalid.name(), "<invalid>");
683        assert_eq!(invalid.qname(), crate::Quark::from_str("<invalid>"));
684        assert!(invalid.is_a(Type::INVALID));
685        assert!(!invalid.is_a(Type::STRING));
686        assert_eq!(invalid.parent(), None);
687        assert!(invalid.children().is_empty());
688        assert!(invalid.interfaces().is_empty());
689        assert!(invalid.interface_prerequisites().is_empty());
690        assert!(!invalid.is_valid());
691        dbg!(&invalid);
692    }
693
694    #[test]
695    fn hash() {
696        // Get this first so the type is registered
697        let iu_type = InitiallyUnowned::static_type();
698
699        let set = Type::OBJECT
700            .children()
701            .iter()
702            .copied()
703            .collect::<HashSet<_>>();
704        assert!(set.contains(&iu_type));
705    }
706
707    #[test]
708    fn ord() {
709        // Get this first so the type is registered
710        let iu_type = InitiallyUnowned::static_type();
711        assert!(Type::OBJECT < iu_type);
712
713        let set = Type::OBJECT
714            .children()
715            .iter()
716            .copied()
717            .collect::<BTreeSet<_>>();
718        assert!(set.contains(&iu_type));
719    }
720}