1 // Take a look at the license at the top of the repository in the LICENSE file.
2
3 //! Runtime type information.
4
5 use crate::translate::{
6 from_glib, FromGlib, FromGlibContainerAsVec, IntoGlib, ToGlibContainerFromSlice, ToGlibPtr,
7 ToGlibPtrMut,
8 };
9
10 use std::fmt;
11 use std::mem;
12 use std::ptr;
13
14 /// A GLib or GLib-based library type
15 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16 #[doc(alias = "GType")]
17 pub struct Type(ffi::GType);
18
19 impl Type {
20 /// An invalid `Type` used as error return value in some functions
21 pub const INVALID: Self = Self(gobject_ffi::G_TYPE_INVALID);
22
23 /// The fundamental type corresponding to the unit type `()`
24 pub const UNIT: Self = Self(gobject_ffi::G_TYPE_NONE);
25
26 /// The fundamental type corresponding to `i8`
27 pub const I8: Self = Self(gobject_ffi::G_TYPE_CHAR);
28
29 /// The fundamental type corresponding to `u8`
30 pub const U8: Self = Self(gobject_ffi::G_TYPE_UCHAR);
31
32 /// The fundamental type corresponding to `bool`
33 pub const BOOL: Self = Self(gobject_ffi::G_TYPE_BOOLEAN);
34
35 /// The fundamental type corresponding to `i32`
36 pub const I32: Self = Self(gobject_ffi::G_TYPE_INT);
37
38 /// The fundamental type corresponding to `u32`
39 pub const U32: Self = Self(gobject_ffi::G_TYPE_UINT);
40
41 /// The fundamental type corresponding to C `long`
42 pub const I_LONG: Self = Self(gobject_ffi::G_TYPE_LONG);
43
44 /// The fundamental type corresponding to C `unsigned long`
45 pub const U_LONG: Self = Self(gobject_ffi::G_TYPE_ULONG);
46
47 /// The fundamental type corresponding to `i64`
48 pub const I64: Self = Self(gobject_ffi::G_TYPE_INT64);
49
50 /// The fundamental type corresponding to `u64`
51 pub const U64: Self = Self(gobject_ffi::G_TYPE_UINT64);
52
53 /// The fundamental type corresponding to `f32`
54 pub const F32: Self = Self(gobject_ffi::G_TYPE_FLOAT);
55
56 /// The fundamental type corresponding to `f64`
57 pub const F64: Self = Self(gobject_ffi::G_TYPE_DOUBLE);
58
59 /// The fundamental type corresponding to `String`
60 pub const STRING: Self = Self(gobject_ffi::G_TYPE_STRING);
61
62 /// The fundamental type corresponding to a pointer
63 pub const POINTER: Self = Self(gobject_ffi::G_TYPE_POINTER);
64
65 /// The fundamental type of GVariant
66 pub const VARIANT: Self = Self(gobject_ffi::G_TYPE_VARIANT);
67
68 /// The fundamental type from which all interfaces are derived
69 pub const INTERFACE: Self = Self(gobject_ffi::G_TYPE_INTERFACE);
70
71 /// The fundamental type from which all enumeration types are derived
72 pub const ENUM: Self = Self(gobject_ffi::G_TYPE_ENUM);
73
74 /// The fundamental type from which all flags types are derived
75 pub const FLAGS: Self = Self(gobject_ffi::G_TYPE_FLAGS);
76
77 /// The fundamental type from which all boxed types are derived
78 pub const BOXED: Self = Self(gobject_ffi::G_TYPE_BOXED);
79
80 /// The fundamental type from which all `GParamSpec` types are derived
81 pub const PARAM_SPEC: Self = Self(gobject_ffi::G_TYPE_PARAM);
82
83 /// The fundamental type from which all objects are derived
84 pub const OBJECT: Self = Self(gobject_ffi::G_TYPE_OBJECT);
85
86 #[doc(alias = "g_type_name")]
name<'a>(self) -> &'a str87 pub fn name<'a>(self) -> &'a str {
88 match self.into_glib() {
89 gobject_ffi::G_TYPE_INVALID => "<invalid>",
90 x => unsafe {
91 let ptr = gobject_ffi::g_type_name(x);
92 std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
93 },
94 }
95 }
96
97 #[doc(alias = "g_type_qname")]
qname(self) -> crate::Quark98 pub fn qname(self) -> crate::Quark {
99 match self.into_glib() {
100 gobject_ffi::G_TYPE_INVALID => crate::Quark::from_string("<invalid>"),
101 x => unsafe { from_glib(gobject_ffi::g_type_qname(x)) },
102 }
103 }
104
105 #[doc(alias = "g_type_is_a")]
is_a(self, other: Self) -> bool106 pub fn is_a(self, other: Self) -> bool {
107 unsafe {
108 from_glib(gobject_ffi::g_type_is_a(
109 self.into_glib(),
110 other.into_glib(),
111 ))
112 }
113 }
114
115 #[doc(alias = "g_type_parent")]
parent(self) -> Option<Self>116 pub fn parent(self) -> Option<Self> {
117 unsafe {
118 let parent: Self = from_glib(gobject_ffi::g_type_parent(self.into_glib()));
119 Some(parent).filter(|t| t.is_valid())
120 }
121 }
122
123 #[doc(alias = "g_type_children")]
children(self) -> Vec<Self>124 pub fn children(self) -> Vec<Self> {
125 unsafe {
126 let mut n_children = 0u32;
127 let children = gobject_ffi::g_type_children(self.into_glib(), &mut n_children);
128
129 FromGlibContainerAsVec::from_glib_full_num_as_vec(children, n_children as usize)
130 }
131 }
132
133 #[doc(alias = "g_type_interfaces")]
interfaces(self) -> Vec<Self>134 pub fn interfaces(self) -> Vec<Self> {
135 unsafe {
136 let mut n_interfaces = 0u32;
137 let interfaces = gobject_ffi::g_type_interfaces(self.into_glib(), &mut n_interfaces);
138
139 FromGlibContainerAsVec::from_glib_full_num_as_vec(interfaces, n_interfaces as usize)
140 }
141 }
142
143 #[doc(alias = "g_type_interface_prerequisites")]
interface_prerequisites(self) -> Vec<Self>144 pub fn interface_prerequisites(self) -> Vec<Self> {
145 match self {
146 t if !t.is_a(Self::INTERFACE) => vec![],
147 _ => unsafe {
148 let mut n_prereqs = 0u32;
149 let prereqs =
150 gobject_ffi::g_type_interface_prerequisites(self.into_glib(), &mut n_prereqs);
151
152 FromGlibContainerAsVec::from_glib_full_num_as_vec(prereqs, n_prereqs as usize)
153 },
154 }
155 }
156
157 #[doc(alias = "g_type_from_name")]
from_name<'a, P: Into<&'a str>>(name: P) -> Option<Self>158 pub fn from_name<'a, P: Into<&'a str>>(name: P) -> Option<Self> {
159 unsafe {
160 let type_: Self =
161 from_glib(gobject_ffi::g_type_from_name(name.into().to_glib_none().0));
162 Some(type_).filter(|t| t.is_valid())
163 }
164 }
165
166 /// Checks that the type is not [`INVALID`](Self::INVALID)
167 #[inline]
is_valid(self) -> bool168 pub fn is_valid(self) -> bool {
169 self != Self::INVALID
170 }
171 }
172
173 impl fmt::Debug for Type {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 f.write_str(self.name())
176 }
177 }
178
179 impl fmt::Display for Type {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result180 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181 f.write_str(self.name())
182 }
183 }
184
185 /// Types that are supported by GLib dynamic typing.
186 pub trait StaticType {
187 /// Returns the type identifier of `Self`.
static_type() -> Type188 fn static_type() -> Type;
189 }
190
191 impl StaticType for Type {
192 #[doc(alias = "g_gtype_get_type")]
static_type() -> Type193 fn static_type() -> Type {
194 unsafe { from_glib(gobject_ffi::g_gtype_get_type()) }
195 }
196 }
197
198 #[doc(hidden)]
199 impl crate::value::ValueType for Type {
200 type Type = Type;
201 }
202
203 #[doc(hidden)]
204 unsafe impl<'a> crate::value::FromValue<'a> for Type {
205 type Checker = crate::value::GenericValueTypeChecker<Self>;
206
from_value(value: &'a crate::Value) -> Self207 unsafe fn from_value(value: &'a crate::Value) -> Self {
208 from_glib(gobject_ffi::g_value_get_gtype(value.to_glib_none().0))
209 }
210 }
211
212 #[doc(hidden)]
213 impl crate::value::ToValue for Type {
to_value(&self) -> crate::Value214 fn to_value(&self) -> crate::Value {
215 unsafe {
216 let mut value = crate::Value::from_type(Type::static_type());
217 gobject_ffi::g_value_set_gtype(value.to_glib_none_mut().0, self.into_glib());
218 value
219 }
220 }
221
value_type(&self) -> crate::Type222 fn value_type(&self) -> crate::Type {
223 Type::static_type()
224 }
225 }
226
227 impl<'a, T: ?Sized + StaticType> StaticType for &'a T {
static_type() -> Type228 fn static_type() -> Type {
229 T::static_type()
230 }
231 }
232
233 impl<'a, T: ?Sized + StaticType> StaticType for &'a mut T {
static_type() -> Type234 fn static_type() -> Type {
235 T::static_type()
236 }
237 }
238
239 macro_rules! builtin {
240 ($name:ty, $val:ident) => {
241 impl StaticType for $name {
242 fn static_type() -> Type {
243 Type::$val
244 }
245 }
246 };
247 }
248
249 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
250 pub struct ILong(pub libc::c_long);
251
252 impl std::ops::Deref for ILong {
253 type Target = libc::c_long;
254
deref(&self) -> &Self::Target255 fn deref(&self) -> &Self::Target {
256 &self.0
257 }
258 }
259
260 impl std::ops::DerefMut for ILong {
deref_mut(&mut self) -> &mut Self::Target261 fn deref_mut(&mut self) -> &mut Self::Target {
262 &mut self.0
263 }
264 }
265
266 impl From<libc::c_long> for ILong {
from(v: libc::c_long) -> ILong267 fn from(v: libc::c_long) -> ILong {
268 ILong(v)
269 }
270 }
271
272 impl From<ILong> for libc::c_long {
from(v: ILong) -> libc::c_long273 fn from(v: ILong) -> libc::c_long {
274 v.0
275 }
276 }
277
278 impl PartialEq<libc::c_long> for ILong {
eq(&self, other: &libc::c_long) -> bool279 fn eq(&self, other: &libc::c_long) -> bool {
280 &self.0 == other
281 }
282 }
283
284 impl PartialEq<ILong> for libc::c_long {
eq(&self, other: &ILong) -> bool285 fn eq(&self, other: &ILong) -> bool {
286 self == &other.0
287 }
288 }
289
290 impl PartialOrd<libc::c_long> for ILong {
partial_cmp(&self, other: &libc::c_long) -> Option<std::cmp::Ordering>291 fn partial_cmp(&self, other: &libc::c_long) -> Option<std::cmp::Ordering> {
292 self.0.partial_cmp(other)
293 }
294 }
295
296 impl PartialOrd<ILong> for libc::c_long {
partial_cmp(&self, other: &ILong) -> Option<std::cmp::Ordering>297 fn partial_cmp(&self, other: &ILong) -> Option<std::cmp::Ordering> {
298 self.partial_cmp(&other.0)
299 }
300 }
301
302 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
303 pub struct ULong(pub libc::c_ulong);
304
305 impl std::ops::Deref for ULong {
306 type Target = libc::c_ulong;
307
deref(&self) -> &Self::Target308 fn deref(&self) -> &Self::Target {
309 &self.0
310 }
311 }
312
313 impl std::ops::DerefMut for ULong {
deref_mut(&mut self) -> &mut Self::Target314 fn deref_mut(&mut self) -> &mut Self::Target {
315 &mut self.0
316 }
317 }
318
319 impl From<libc::c_ulong> for ULong {
from(v: libc::c_ulong) -> ULong320 fn from(v: libc::c_ulong) -> ULong {
321 ULong(v)
322 }
323 }
324
325 impl From<ULong> for libc::c_ulong {
from(v: ULong) -> libc::c_ulong326 fn from(v: ULong) -> libc::c_ulong {
327 v.0
328 }
329 }
330
331 impl PartialEq<libc::c_ulong> for ULong {
eq(&self, other: &libc::c_ulong) -> bool332 fn eq(&self, other: &libc::c_ulong) -> bool {
333 &self.0 == other
334 }
335 }
336
337 impl PartialEq<ULong> for libc::c_ulong {
eq(&self, other: &ULong) -> bool338 fn eq(&self, other: &ULong) -> bool {
339 self == &other.0
340 }
341 }
342
343 impl PartialOrd<libc::c_ulong> for ULong {
partial_cmp(&self, other: &libc::c_ulong) -> Option<std::cmp::Ordering>344 fn partial_cmp(&self, other: &libc::c_ulong) -> Option<std::cmp::Ordering> {
345 self.0.partial_cmp(other)
346 }
347 }
348
349 impl PartialOrd<ULong> for libc::c_ulong {
partial_cmp(&self, other: &ULong) -> Option<std::cmp::Ordering>350 fn partial_cmp(&self, other: &ULong) -> Option<std::cmp::Ordering> {
351 self.partial_cmp(&other.0)
352 }
353 }
354
355 builtin!(bool, BOOL);
356 builtin!(i8, I8);
357 builtin!(u8, U8);
358 builtin!(i32, I32);
359 builtin!(u32, U32);
360 builtin!(i64, I64);
361 builtin!(u64, U64);
362 builtin!(ILong, I_LONG);
363 builtin!(ULong, U_LONG);
364 builtin!(f32, F32);
365 builtin!(f64, F64);
366 builtin!(str, STRING);
367 builtin!(String, STRING);
368
369 impl<'a> StaticType for [&'a str] {
static_type() -> Type370 fn static_type() -> Type {
371 unsafe { from_glib(ffi::g_strv_get_type()) }
372 }
373 }
374
375 impl StaticType for Vec<String> {
static_type() -> Type376 fn static_type() -> Type {
377 unsafe { from_glib(ffi::g_strv_get_type()) }
378 }
379 }
380
381 impl StaticType for () {
static_type() -> Type382 fn static_type() -> Type {
383 Type::UNIT
384 }
385 }
386
387 #[inline]
instance_of<C: StaticType>(ptr: ffi::gconstpointer) -> bool388 pub unsafe fn instance_of<C: StaticType>(ptr: ffi::gconstpointer) -> bool {
389 from_glib(gobject_ffi::g_type_check_instance_is_a(
390 ptr as *mut _,
391 <C as StaticType>::static_type().into_glib(),
392 ))
393 }
394
395 impl FromGlib<ffi::GType> for Type {
396 #[inline]
from_glib(val: ffi::GType) -> Self397 unsafe fn from_glib(val: ffi::GType) -> Self {
398 Self(val)
399 }
400 }
401
402 impl IntoGlib for Type {
403 type GlibType = ffi::GType;
404
405 #[inline]
into_glib(self) -> ffi::GType406 fn into_glib(self) -> ffi::GType {
407 self.0
408 }
409 }
410
411 impl<'a> ToGlibContainerFromSlice<'a, *mut ffi::GType> for Type {
412 type Storage = Option<Vec<ffi::GType>>;
413
to_glib_none_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage)414 fn to_glib_none_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
415 let mut vec = t.iter().map(|t| t.into_glib()).collect::<Vec<_>>();
416
417 (vec.as_mut_ptr(), Some(vec))
418 }
419
to_glib_container_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage)420 fn to_glib_container_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
421 (Self::to_glib_full_from_slice(t), None)
422 }
423
to_glib_full_from_slice(t: &[Type]) -> *mut ffi::GType424 fn to_glib_full_from_slice(t: &[Type]) -> *mut ffi::GType {
425 if t.is_empty() {
426 return ptr::null_mut();
427 }
428
429 unsafe {
430 let res =
431 ffi::g_malloc0(mem::size_of::<ffi::GType>() * (t.len() + 1)) as *mut ffi::GType;
432 for (i, v) in t.iter().enumerate() {
433 *res.add(i) = v.into_glib();
434 }
435 res
436 }
437 }
438 }
439
440 impl FromGlibContainerAsVec<Type, *const ffi::GType> for Type {
from_glib_none_num_as_vec(ptr: *const ffi::GType, num: usize) -> Vec<Self>441 unsafe fn from_glib_none_num_as_vec(ptr: *const ffi::GType, num: usize) -> Vec<Self> {
442 if num == 0 || ptr.is_null() {
443 return Vec::new();
444 }
445
446 let mut res = Vec::with_capacity(num);
447 for i in 0..num {
448 res.push(from_glib(*ptr.add(i)));
449 }
450 res
451 }
452
from_glib_container_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self>453 unsafe fn from_glib_container_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
454 // Can't really free a *const
455 unimplemented!();
456 }
457
from_glib_full_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self>458 unsafe fn from_glib_full_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
459 // Can't really free a *const
460 unimplemented!();
461 }
462 }
463
464 impl FromGlibContainerAsVec<Type, *mut ffi::GType> for Type {
from_glib_none_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self>465 unsafe fn from_glib_none_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
466 FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *const _, num)
467 }
468
from_glib_container_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self>469 unsafe fn from_glib_container_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
470 let res = FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
471 ffi::g_free(ptr as *mut _);
472 res
473 }
474
from_glib_full_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self>475 unsafe fn from_glib_full_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
476 FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, num)
477 }
478 }
479
480 #[cfg(test)]
481 mod tests {
482 use super::*;
483 use crate::InitiallyUnowned;
484 use std::collections::{BTreeSet, HashSet};
485
486 #[test]
invalid()487 fn invalid() {
488 let invalid = Type::INVALID;
489
490 assert_eq!(invalid.name(), "<invalid>");
491 assert_eq!(invalid.qname(), crate::Quark::from_string("<invalid>"));
492 assert!(invalid.is_a(Type::INVALID));
493 assert!(!invalid.is_a(Type::STRING));
494 assert_eq!(invalid.parent(), None);
495 assert_eq!(invalid.children(), vec![]);
496 assert_eq!(invalid.interfaces(), vec![]);
497 assert_eq!(invalid.interface_prerequisites(), vec![]);
498 assert!(!invalid.is_valid());
499 dbg!(&invalid);
500 }
501
502 #[test]
hash()503 fn hash() {
504 // Get this first so the type is registered
505 let iu_type = InitiallyUnowned::static_type();
506
507 let set = Type::OBJECT.children().into_iter().collect::<HashSet<_>>();
508 assert!(set.contains(&iu_type));
509 }
510
511 #[test]
ord()512 fn ord() {
513 // Get this first so the type is registered
514 let iu_type = InitiallyUnowned::static_type();
515 assert!(Type::OBJECT < iu_type);
516
517 let set = Type::OBJECT.children().into_iter().collect::<BTreeSet<_>>();
518 assert!(set.contains(&iu_type));
519 }
520 }
521