Skip to main content

glib/
enums.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cmp, ffi::CStr, fmt, ops::Deref, ptr};
4
5use crate::{
6    ParamSpecEnum, ParamSpecFlags, Type, TypeInfo, Value, ffi, gobject_ffi, prelude::*,
7    translate::*,
8};
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
11pub enum UserDirectory {
12    #[doc(alias = "G_USER_DIRECTORY_DESKTOP")]
13    Desktop,
14    #[doc(alias = "G_USER_DIRECTORY_DOCUMENTS")]
15    Documents,
16    #[doc(alias = "G_USER_DIRECTORY_DOWNLOAD")]
17    Downloads,
18    #[doc(alias = "G_USER_DIRECTORY_MUSIC")]
19    Music,
20    #[doc(alias = "G_USER_DIRECTORY_PICTURES")]
21    Pictures,
22    #[doc(alias = "G_USER_DIRECTORY_PUBLIC_SHARE")]
23    PublicShare,
24    #[doc(alias = "G_USER_DIRECTORY_TEMPLATES")]
25    Templates,
26    #[doc(alias = "G_USER_DIRECTORY_VIDEOS")]
27    Videos,
28}
29
30#[doc(hidden)]
31impl IntoGlib for UserDirectory {
32    type GlibType = ffi::GUserDirectory;
33
34    #[inline]
35    fn into_glib(self) -> ffi::GUserDirectory {
36        match self {
37            Self::Desktop => ffi::G_USER_DIRECTORY_DESKTOP,
38            Self::Documents => ffi::G_USER_DIRECTORY_DOCUMENTS,
39            Self::Downloads => ffi::G_USER_DIRECTORY_DOWNLOAD,
40            Self::Music => ffi::G_USER_DIRECTORY_MUSIC,
41            Self::Pictures => ffi::G_USER_DIRECTORY_PICTURES,
42            Self::PublicShare => ffi::G_USER_DIRECTORY_PUBLIC_SHARE,
43            Self::Templates => ffi::G_USER_DIRECTORY_TEMPLATES,
44            Self::Videos => ffi::G_USER_DIRECTORY_VIDEOS,
45        }
46    }
47}
48
49// rustdoc-stripper-ignore-next
50/// Representation of an `enum` for dynamically, at runtime, querying the values of the enum and
51/// using them.
52#[doc(alias = "GEnumClass")]
53#[repr(transparent)]
54pub struct EnumClass(ptr::NonNull<gobject_ffi::GEnumClass>);
55
56unsafe impl Send for EnumClass {}
57unsafe impl Sync for EnumClass {}
58
59impl fmt::Debug for EnumClass {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.debug_struct("EnumClass")
62            .field("type", &self.type_())
63            .field("values", &self.values())
64            .finish()
65    }
66}
67
68impl EnumClass {
69    // rustdoc-stripper-ignore-next
70    /// Create a new `EnumClass` from a static type `T`.
71    ///
72    /// Panics if `T` is not representing an enum.
73    pub fn new<T: StaticType + HasParamSpec<ParamSpec = ParamSpecEnum>>() -> Self {
74        Self::with_type(T::static_type()).expect("invalid enum class")
75    }
76    // rustdoc-stripper-ignore-next
77    /// Create a new `EnumClass` from a `Type`.
78    ///
79    /// Returns `None` if `type_` is not representing an enum.
80    pub fn with_type(type_: Type) -> Option<Self> {
81        unsafe {
82            let is_enum: bool = from_glib(gobject_ffi::g_type_is_a(
83                type_.into_glib(),
84                gobject_ffi::G_TYPE_ENUM,
85            ));
86            if !is_enum {
87                return None;
88            }
89
90            Some(EnumClass(
91                ptr::NonNull::new(gobject_ffi::g_type_class_ref(type_.into_glib()) as *mut _)
92                    .unwrap(),
93            ))
94        }
95    }
96
97    // rustdoc-stripper-ignore-next
98    /// `Type` of the enum.
99    pub fn type_(&self) -> Type {
100        unsafe { from_glib(self.0.as_ref().g_type_class.g_type) }
101    }
102
103    // rustdoc-stripper-ignore-next
104    /// Gets `EnumValue` by integer `value`, if existing.
105    ///
106    /// Returns `None` if the enum does not contain any value
107    /// with `value`.
108    #[doc(alias = "g_enum_get_value")]
109    #[doc(alias = "get_value")]
110    pub fn value(&self, value: i32) -> Option<&EnumValue> {
111        unsafe {
112            let v = gobject_ffi::g_enum_get_value(self.0.as_ptr(), value);
113            if v.is_null() {
114                None
115            } else {
116                Some(&*(v as *const EnumValue))
117            }
118        }
119    }
120
121    // rustdoc-stripper-ignore-next
122    /// Gets `EnumValue` by string name `name`, if existing.
123    ///
124    /// Returns `None` if the enum does not contain any value
125    /// with name `name`.
126    #[doc(alias = "g_enum_get_value_by_name")]
127    #[doc(alias = "get_value_by_name")]
128    pub fn value_by_name(&self, name: &str) -> Option<&EnumValue> {
129        unsafe {
130            let v = gobject_ffi::g_enum_get_value_by_name(self.0.as_ptr(), name.to_glib_none().0);
131            if v.is_null() {
132                None
133            } else {
134                Some(&*(v as *const EnumValue))
135            }
136        }
137    }
138
139    // rustdoc-stripper-ignore-next
140    /// Gets `EnumValue` by string nick `nick`, if existing.
141    ///
142    /// Returns `None` if the enum does not contain any value
143    /// with nick `nick`.
144    #[doc(alias = "g_enum_get_value_by_nick")]
145    #[doc(alias = "get_value_by_nick")]
146    pub fn value_by_nick(&self, nick: &str) -> Option<&EnumValue> {
147        unsafe {
148            let v = gobject_ffi::g_enum_get_value_by_nick(self.0.as_ptr(), nick.to_glib_none().0);
149            if v.is_null() {
150                None
151            } else {
152                Some(&*(v as *const EnumValue))
153            }
154        }
155    }
156
157    // rustdoc-stripper-ignore-next
158    /// Gets all `EnumValue` of this `EnumClass`.
159    #[doc(alias = "get_values")]
160    pub fn values(&self) -> &[EnumValue] {
161        unsafe {
162            if self.0.as_ref().n_values == 0 {
163                return &[];
164            }
165            std::slice::from_raw_parts(
166                self.0.as_ref().values as *const EnumValue,
167                self.0.as_ref().n_values as usize,
168            )
169        }
170    }
171
172    // rustdoc-stripper-ignore-next
173    /// Converts integer `value` to a `Value`, if part of the enum.
174    pub fn to_value(&self, value: i32) -> Option<Value> {
175        self.value(value).map(|v| v.to_value(self))
176    }
177
178    // rustdoc-stripper-ignore-next
179    /// Converts string name `name` to a `Value`, if part of the enum.
180    pub fn to_value_by_name(&self, name: &str) -> Option<Value> {
181        self.value_by_name(name).map(|v| v.to_value(self))
182    }
183
184    // rustdoc-stripper-ignore-next
185    /// Converts string nick `nick` to a `Value`, if part of the enum.
186    pub fn to_value_by_nick(&self, nick: &str) -> Option<Value> {
187        self.value_by_nick(nick).map(|v| v.to_value(self))
188    }
189
190    // rustdoc-stripper-ignore-next
191    /// Complete `TypeInfo` for an enum with values.
192    /// This is an associated function. A method would result in a stack overflow due to a recurvice call:
193    /// callers should first create an `EnumClass` instance by calling `EnumClass::with_type()` which indirectly
194    /// calls `TypePluginRegisterImpl::register_dynamic_enum()` and `TypePluginImpl::complete_type_info()`
195    /// and one of them should call `EnumClass::with_type()` before calling this method.
196    /// `const_static_values` is a reference on a wrapper of a slice of `EnumValue`.
197    /// It must be static to ensure enumeration values are never dropped, and ensures that slice is terminated
198    ///  by an `EnumValue` with all members being 0, as expected by GLib.
199    #[doc(alias = "g_enum_complete_type_info")]
200    pub fn complete_type_info(
201        type_: Type,
202        const_static_values: &'static EnumValues,
203    ) -> Option<TypeInfo> {
204        unsafe {
205            let is_enum: bool = from_glib(gobject_ffi::g_type_is_a(
206                type_.into_glib(),
207                gobject_ffi::G_TYPE_ENUM,
208            ));
209            if !is_enum {
210                return None;
211            }
212
213            let info = TypeInfo::default();
214            gobject_ffi::g_enum_complete_type_info(
215                type_.into_glib(),
216                info.as_ptr(),
217                const_static_values.to_glib_none().0,
218            );
219            Some(info)
220        }
221    }
222}
223
224impl Drop for EnumClass {
225    #[inline]
226    fn drop(&mut self) {
227        unsafe {
228            gobject_ffi::g_type_class_unref(self.0.as_ptr() as *mut _);
229        }
230    }
231}
232
233impl Clone for EnumClass {
234    #[inline]
235    fn clone(&self) -> Self {
236        unsafe {
237            Self(ptr::NonNull::new(gobject_ffi::g_type_class_ref(self.type_().into_glib()) as *mut _).unwrap())
238        }
239    }
240}
241
242// rustdoc-stripper-ignore-next
243/// Representation of a single enum value of an `EnumClass`.
244#[doc(alias = "GEnumValue")]
245#[derive(Copy, Clone)]
246#[repr(transparent)]
247pub struct EnumValue(gobject_ffi::GEnumValue);
248
249unsafe impl Send for EnumValue {}
250unsafe impl Sync for EnumValue {}
251
252impl fmt::Debug for EnumValue {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        f.debug_struct("EnumValue")
255            .field("value", &self.value())
256            .field("name", &self.name())
257            .field("nick", &self.nick())
258            .finish()
259    }
260}
261
262impl EnumValue {
263    // rustdoc-stripper-ignore-next
264    /// # Safety
265    ///
266    /// It is the responsibility of the caller to ensure `GEnumValue` is
267    /// valid.
268    pub const unsafe fn unsafe_from(g_value: gobject_ffi::GEnumValue) -> Self {
269        Self(g_value)
270    }
271
272    // rustdoc-stripper-ignore-next
273    /// Get integer value corresponding to the value.
274    #[doc(alias = "get_value")]
275    pub fn value(&self) -> i32 {
276        self.0.value
277    }
278
279    // rustdoc-stripper-ignore-next
280    /// Get name corresponding to the value.
281    #[doc(alias = "get_name")]
282    pub fn name<'a>(&self) -> &'a str {
283        unsafe { CStr::from_ptr(self.0.value_name).to_str().unwrap() }
284    }
285
286    // rustdoc-stripper-ignore-next
287    /// Get nick corresponding to the value.
288    #[doc(alias = "get_nick")]
289    pub fn nick<'a>(&self) -> &'a str {
290        unsafe { CStr::from_ptr(self.0.value_nick).to_str().unwrap() }
291    }
292
293    // rustdoc-stripper-ignore-next
294    /// Convert enum value to a `Value`.
295    pub fn to_value(&self, enum_: &EnumClass) -> Value {
296        unsafe {
297            let mut v = Value::from_type_unchecked(enum_.type_());
298            gobject_ffi::g_value_set_enum(v.to_glib_none_mut().0, self.0.value);
299            v
300        }
301    }
302
303    // rustdoc-stripper-ignore-next
304    /// Convert enum value from a `Value`.
305    pub fn from_value(value: &Value) -> Option<(EnumClass, &EnumValue)> {
306        unsafe {
307            let enum_class = EnumClass::with_type(value.type_())?;
308            let v = enum_class.value(gobject_ffi::g_value_get_enum(value.to_glib_none().0))?;
309            let v = &*(v as *const EnumValue);
310            Some((enum_class, v))
311        }
312    }
313}
314
315impl PartialEq for EnumValue {
316    fn eq(&self, other: &Self) -> bool {
317        self.value().eq(&other.value())
318    }
319}
320
321impl Eq for EnumValue {}
322
323impl PartialOrd for EnumValue {
324    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
325        Some(self.cmp(other))
326    }
327}
328
329impl Ord for EnumValue {
330    fn cmp(&self, other: &Self) -> cmp::Ordering {
331        self.value().cmp(&other.value())
332    }
333}
334
335impl UnsafeFrom<gobject_ffi::GEnumValue> for EnumValue {
336    unsafe fn unsafe_from(g_value: gobject_ffi::GEnumValue) -> Self {
337        unsafe { Self::unsafe_from(g_value) }
338    }
339}
340
341unsafe impl<'a> crate::value::FromValue<'a> for &EnumValue {
342    type Checker = EnumTypeChecker;
343
344    unsafe fn from_value(value: &'a Value) -> Self {
345        unsafe {
346            let (_, v) = EnumValue::from_value(value).unwrap();
347            // SAFETY: The enum class and its values live forever
348            std::mem::transmute(v)
349        }
350    }
351}
352
353// rustdoc-stripper-ignore-next
354/// Define the zero value and the associated GLib type.
355impl EnumerationValue<EnumValue> for EnumValue {
356    type GlibType = gobject_ffi::GEnumValue;
357    const ZERO: EnumValue = unsafe {
358        EnumValue::unsafe_from(gobject_ffi::GEnumValue {
359            value: 0,
360            value_name: ptr::null(),
361            value_nick: ptr::null(),
362        })
363    };
364}
365
366// rustdoc-stripper-ignore-next
367/// Storage of enum values.
368pub type EnumValuesStorage<const N: usize> = EnumerationValuesStorage<EnumValue, N>;
369
370// rustdoc-stripper-ignore-next
371/// Representation of enum values wrapped by `EnumValuesStorage`
372pub type EnumValues = EnumerationValues<EnumValue>;
373
374pub struct EnumTypeChecker();
375unsafe impl crate::value::ValueTypeChecker for EnumTypeChecker {
376    type Error = InvalidEnumError;
377
378    fn check(value: &Value) -> Result<(), Self::Error> {
379        let t = value.type_();
380        if t.is_a(Type::ENUM) {
381            Ok(())
382        } else {
383            Err(InvalidEnumError)
384        }
385    }
386}
387
388// rustdoc-stripper-ignore-next
389/// An error returned from the [`get`](struct.Value.html#method.get) function
390/// on a [`Value`](struct.Value.html) for enum types.
391#[derive(Clone, PartialEq, Eq, Debug)]
392pub struct InvalidEnumError;
393
394impl fmt::Display for InvalidEnumError {
395    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
396        write!(f, "Value is not an enum")
397    }
398}
399
400impl std::error::Error for InvalidEnumError {}
401
402// rustdoc-stripper-ignore-next
403/// Representation of a `flags` for dynamically, at runtime, querying the values of the enum and
404/// using them
405#[doc(alias = "GFlagsClass")]
406#[repr(transparent)]
407pub struct FlagsClass(ptr::NonNull<gobject_ffi::GFlagsClass>);
408
409unsafe impl Send for FlagsClass {}
410unsafe impl Sync for FlagsClass {}
411
412impl fmt::Debug for FlagsClass {
413    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414        f.debug_struct("FlagsClass")
415            .field("type", &self.type_())
416            .field("values", &self.values())
417            .finish()
418    }
419}
420
421impl FlagsClass {
422    // rustdoc-stripper-ignore-next
423    /// Create a new `FlagsClass` from a static type `T`.
424    ///
425    /// Panics if `T` is not representing an flags type.
426    pub fn new<T: StaticType + HasParamSpec<ParamSpec = ParamSpecFlags>>() -> Self {
427        Self::with_type(T::static_type()).expect("invalid flags class")
428    }
429    // rustdoc-stripper-ignore-next
430    /// Create a new `FlagsClass` from a `Type`
431    ///
432    /// Returns `None` if `type_` is not representing a flags type.
433    pub fn with_type(type_: Type) -> Option<Self> {
434        unsafe {
435            let is_flags: bool = from_glib(gobject_ffi::g_type_is_a(
436                type_.into_glib(),
437                gobject_ffi::G_TYPE_FLAGS,
438            ));
439            if !is_flags {
440                return None;
441            }
442
443            Some(FlagsClass(
444                ptr::NonNull::new(gobject_ffi::g_type_class_ref(type_.into_glib()) as *mut _)
445                    .unwrap(),
446            ))
447        }
448    }
449
450    // rustdoc-stripper-ignore-next
451    /// `Type` of the flags.
452    pub fn type_(&self) -> Type {
453        unsafe { from_glib(self.0.as_ref().g_type_class.g_type) }
454    }
455
456    // rustdoc-stripper-ignore-next
457    /// Gets `FlagsValue` by integer `value`, if existing.
458    ///
459    /// Returns `None` if the flags do not contain any value
460    /// with `value`.
461    #[doc(alias = "g_flags_get_first_value")]
462    #[doc(alias = "get_value")]
463    pub fn value(&self, value: u32) -> Option<&FlagsValue> {
464        unsafe {
465            let v = gobject_ffi::g_flags_get_first_value(self.0.as_ptr(), value);
466            if v.is_null() {
467                None
468            } else {
469                Some(&*(v as *const FlagsValue))
470            }
471        }
472    }
473
474    // rustdoc-stripper-ignore-next
475    /// Gets `FlagsValue` by string name `name`, if existing.
476    ///
477    /// Returns `None` if the flags do not contain any value
478    /// with name `name`.
479    #[doc(alias = "g_flags_get_value_by_name")]
480    #[doc(alias = "get_value_by_name")]
481    pub fn value_by_name(&self, name: &str) -> Option<&FlagsValue> {
482        unsafe {
483            let v = gobject_ffi::g_flags_get_value_by_name(self.0.as_ptr(), name.to_glib_none().0);
484            if v.is_null() {
485                None
486            } else {
487                Some(&*(v as *const FlagsValue))
488            }
489        }
490    }
491
492    // rustdoc-stripper-ignore-next
493    /// Gets `FlagsValue` by string nick `nick`, if existing.
494    ///
495    /// Returns `None` if the flags do not contain any value
496    /// with nick `nick`.
497    #[doc(alias = "g_flags_get_value_by_nick")]
498    #[doc(alias = "get_value_by_nick")]
499    pub fn value_by_nick(&self, nick: &str) -> Option<&FlagsValue> {
500        unsafe {
501            let v = gobject_ffi::g_flags_get_value_by_nick(self.0.as_ptr(), nick.to_glib_none().0);
502            if v.is_null() {
503                None
504            } else {
505                Some(&*(v as *const FlagsValue))
506            }
507        }
508    }
509
510    // rustdoc-stripper-ignore-next
511    /// Gets all `FlagsValue` of this `FlagsClass`.
512    #[doc(alias = "get_values")]
513    pub fn values(&self) -> &[FlagsValue] {
514        unsafe {
515            if self.0.as_ref().n_values == 0 {
516                return &[];
517            }
518            std::slice::from_raw_parts(
519                self.0.as_ref().values as *const FlagsValue,
520                self.0.as_ref().n_values as usize,
521            )
522        }
523    }
524
525    // rustdoc-stripper-ignore-next
526    /// Converts integer `value` to a `Value`, if part of the flags.
527    pub fn to_value(&self, value: u32) -> Option<Value> {
528        self.value(value).map(|v| v.to_value(self))
529    }
530
531    // rustdoc-stripper-ignore-next
532    /// Converts string name `name` to a `Value`, if part of the flags.
533    pub fn to_value_by_name(&self, name: &str) -> Option<Value> {
534        self.value_by_name(name).map(|v| v.to_value(self))
535    }
536
537    // rustdoc-stripper-ignore-next
538    /// Converts string nick `nick` to a `Value`, if part of the flags.
539    pub fn to_value_by_nick(&self, nick: &str) -> Option<Value> {
540        self.value_by_nick(nick).map(|v| v.to_value(self))
541    }
542
543    // rustdoc-stripper-ignore-next
544    /// Checks if the flags corresponding to integer `f` is set in `value`.
545    pub fn is_set(&self, value: &Value, f: u32) -> bool {
546        unsafe {
547            if self.type_() != value.type_() {
548                return false;
549            }
550
551            let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
552            flags & f != 0
553        }
554    }
555
556    // rustdoc-stripper-ignore-next
557    /// Checks if the flags corresponding to string name `name` is set in `value`.
558    pub fn is_set_by_name(&self, value: &Value, name: &str) -> bool {
559        unsafe {
560            if self.type_() != value.type_() {
561                return false;
562            }
563
564            if let Some(f) = self.value_by_name(name) {
565                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
566                flags & f.value() != 0
567            } else {
568                false
569            }
570        }
571    }
572
573    // rustdoc-stripper-ignore-next
574    /// Checks if the flags corresponding to string nick `nick` is set in `value`.
575    pub fn is_set_by_nick(&self, value: &Value, nick: &str) -> bool {
576        unsafe {
577            if self.type_() != value.type_() {
578                return false;
579            }
580
581            if let Some(f) = self.value_by_nick(nick) {
582                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
583                flags & f.value() != 0
584            } else {
585                false
586            }
587        }
588    }
589
590    // rustdoc-stripper-ignore-next
591    /// Set flags value corresponding to integer `f` in `value`, if part of that flags. If the
592    /// flag is already set, it will succeed without doing any changes.
593    ///
594    /// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
595    /// value otherwise.
596    #[doc(alias = "g_value_set_flags")]
597    pub fn set(&self, mut value: Value, f: u32) -> Result<Value, Value> {
598        unsafe {
599            if self.type_() != value.type_() {
600                return Err(value);
601            }
602
603            if let Some(f) = self.value(f) {
604                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
605                gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
606                Ok(value)
607            } else {
608                Err(value)
609            }
610        }
611    }
612
613    // rustdoc-stripper-ignore-next
614    /// Set flags value corresponding to string name `name` in `value`, if part of that flags.
615    /// If the flag is already set, it will succeed without doing any changes.
616    ///
617    /// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
618    /// value otherwise.
619    pub fn set_by_name(&self, mut value: Value, name: &str) -> Result<Value, Value> {
620        unsafe {
621            if self.type_() != value.type_() {
622                return Err(value);
623            }
624
625            if let Some(f) = self.value_by_name(name) {
626                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
627                gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
628                Ok(value)
629            } else {
630                Err(value)
631            }
632        }
633    }
634
635    // rustdoc-stripper-ignore-next
636    /// Set flags value corresponding to string nick `nick` in `value`, if part of that flags.
637    /// If the flag is already set, it will succeed without doing any changes.
638    ///
639    /// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
640    /// value otherwise.
641    pub fn set_by_nick(&self, mut value: Value, nick: &str) -> Result<Value, Value> {
642        unsafe {
643            if self.type_() != value.type_() {
644                return Err(value);
645            }
646
647            if let Some(f) = self.value_by_nick(nick) {
648                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
649                gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
650                Ok(value)
651            } else {
652                Err(value)
653            }
654        }
655    }
656
657    // rustdoc-stripper-ignore-next
658    /// Unset flags value corresponding to integer `f` in `value`, if part of that flags.
659    /// If the flag is already unset, it will succeed without doing any changes.
660    ///
661    /// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
662    /// value otherwise.
663    pub fn unset(&self, mut value: Value, f: u32) -> Result<Value, Value> {
664        unsafe {
665            if self.type_() != value.type_() {
666                return Err(value);
667            }
668
669            if let Some(f) = self.value(f) {
670                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
671                gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
672                Ok(value)
673            } else {
674                Err(value)
675            }
676        }
677    }
678
679    // rustdoc-stripper-ignore-next
680    /// Unset flags value corresponding to string name `name` in `value`, if part of that flags.
681    /// If the flag is already unset, it will succeed without doing any changes.
682    ///
683    /// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
684    /// value otherwise.
685    pub fn unset_by_name(&self, mut value: Value, name: &str) -> Result<Value, Value> {
686        unsafe {
687            if self.type_() != value.type_() {
688                return Err(value);
689            }
690
691            if let Some(f) = self.value_by_name(name) {
692                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
693                gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
694                Ok(value)
695            } else {
696                Err(value)
697            }
698        }
699    }
700
701    // rustdoc-stripper-ignore-next
702    /// Unset flags value corresponding to string nick `nick` in `value`, if part of that flags.
703    /// If the flag is already unset, it will succeed without doing any changes.
704    ///
705    /// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
706    /// value otherwise.
707    pub fn unset_by_nick(&self, mut value: Value, nick: &str) -> Result<Value, Value> {
708        unsafe {
709            if self.type_() != value.type_() {
710                return Err(value);
711            }
712
713            if let Some(f) = self.value_by_nick(nick) {
714                let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
715                gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
716                Ok(value)
717            } else {
718                Err(value)
719            }
720        }
721    }
722
723    // rustdoc-stripper-ignore-next
724    /// Converts an integer `value` to a string of nicks separated by `|`.
725    pub fn to_nick_string(&self, mut value: u32) -> String {
726        let mut s = String::new();
727        for val in self.values() {
728            let v = val.value();
729            if v != 0 && (value & v) == v {
730                value &= !v;
731                if !s.is_empty() {
732                    s.push('|');
733                }
734                s.push_str(val.nick());
735            }
736        }
737        s
738    }
739
740    // rustdoc-stripper-ignore-next
741    /// Converts a string of nicks `s` separated by `|` to an integer value.
742    pub fn from_nick_string(&self, s: &str) -> Result<u32, ParseFlagsError> {
743        s.split('|').try_fold(0u32, |acc, flag| {
744            self.value_by_nick(flag.trim())
745                .map(|v| acc + v.value())
746                .ok_or_else(|| ParseFlagsError(flag.to_owned()))
747        })
748    }
749
750    // rustdoc-stripper-ignore-next
751    /// Returns a new `FlagsBuilder` for conveniently setting/unsetting flags
752    /// and building a `Value`.
753    pub fn builder(&self) -> FlagsBuilder<'_> {
754        FlagsBuilder::new(self)
755    }
756
757    // rustdoc-stripper-ignore-next
758    /// Returns a new `FlagsBuilder` for conveniently setting/unsetting flags
759    /// and building a `Value`. The `Value` is initialized with `value`.
760    pub fn builder_with_value(&self, value: Value) -> Option<FlagsBuilder<'_>> {
761        if self.type_() != value.type_() {
762            return None;
763        }
764
765        Some(FlagsBuilder::with_value(self, value))
766    }
767
768    // rustdoc-stripper-ignore-next
769    /// Complete `TypeInfo` for the flags with values.
770    /// This is an associated function. A method would result in a stack overflow due to a recurvice call:
771    /// callers should first create an `FlagsClass` instance by calling `FlagsClass::with_type()` which indirectly
772    /// calls `TypePluginRegisterImpl::register_dynamic_flags()` and `TypePluginImpl::complete_type_info()`
773    /// and one of them should call `FlagsClass::with_type()` before calling this method.
774    /// `const_static_values` is a reference on a wrapper of a slice of `FlagsValue`.
775    /// It must be static to ensure flags values are never dropped, and ensures that slice is terminated
776    ///  by an `FlagsValue` with all members being 0, as expected by GLib.
777    #[doc(alias = "g_flags_complete_type_info")]
778    pub fn complete_type_info(
779        type_: Type,
780        const_static_values: &'static FlagsValues,
781    ) -> Option<TypeInfo> {
782        unsafe {
783            let is_flags: bool = from_glib(gobject_ffi::g_type_is_a(
784                type_.into_glib(),
785                gobject_ffi::G_TYPE_FLAGS,
786            ));
787            if !is_flags {
788                return None;
789            }
790
791            let info = TypeInfo::default();
792            gobject_ffi::g_flags_complete_type_info(
793                type_.into_glib(),
794                info.as_ptr(),
795                const_static_values.to_glib_none().0,
796            );
797            Some(info)
798        }
799    }
800}
801
802impl Drop for FlagsClass {
803    #[inline]
804    fn drop(&mut self) {
805        unsafe {
806            gobject_ffi::g_type_class_unref(self.0.as_ptr() as *mut _);
807        }
808    }
809}
810
811impl Clone for FlagsClass {
812    #[inline]
813    fn clone(&self) -> Self {
814        unsafe {
815            Self(ptr::NonNull::new(gobject_ffi::g_type_class_ref(self.type_().into_glib()) as *mut _).unwrap())
816        }
817    }
818}
819
820#[derive(Debug)]
821pub struct ParseFlagsError(String);
822
823impl std::error::Error for ParseFlagsError {}
824
825impl fmt::Display for ParseFlagsError {
826    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
827        write!(f, "Unknown flag: '{}'", self.0)
828    }
829}
830
831impl ParseFlagsError {
832    pub fn flag(&self) -> &str {
833        &self.0
834    }
835}
836
837// rustdoc-stripper-ignore-next
838/// Representation of a single flags value of a `FlagsClass`.
839#[doc(alias = "GFlagsValue")]
840#[derive(Copy, Clone)]
841#[repr(transparent)]
842pub struct FlagsValue(gobject_ffi::GFlagsValue);
843
844unsafe impl Send for FlagsValue {}
845unsafe impl Sync for FlagsValue {}
846
847impl fmt::Debug for FlagsValue {
848    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
849        f.debug_struct("FlagsValue")
850            .field("value", &self.value())
851            .field("name", &self.name())
852            .field("nick", &self.nick())
853            .finish()
854    }
855}
856
857impl FlagsValue {
858    // rustdoc-stripper-ignore-next
859    /// # Safety
860    ///
861    /// It is the responsibility of the caller to ensure `GFlagsValue` is
862    /// valid.
863    pub const unsafe fn unsafe_from(g_value: gobject_ffi::GFlagsValue) -> Self {
864        Self(g_value)
865    }
866
867    // rustdoc-stripper-ignore-next
868    /// Get integer value corresponding to the value.
869    #[doc(alias = "get_value")]
870    pub fn value(&self) -> u32 {
871        self.0.value
872    }
873
874    // rustdoc-stripper-ignore-next
875    /// Get name corresponding to the value.
876    #[doc(alias = "get_name")]
877    pub fn name<'a>(&self) -> &'a str {
878        unsafe { CStr::from_ptr(self.0.value_name).to_str().unwrap() }
879    }
880
881    // rustdoc-stripper-ignore-next
882    /// Get nick corresponding to the value.
883    #[doc(alias = "get_nick")]
884    pub fn nick<'a>(&self) -> &'a str {
885        unsafe { CStr::from_ptr(self.0.value_nick).to_str().unwrap() }
886    }
887
888    // rustdoc-stripper-ignore-next
889    /// Convert flags value to a `Value`.
890    pub fn to_value(&self, flags: &FlagsClass) -> Value {
891        unsafe {
892            let mut v = Value::from_type_unchecked(flags.type_());
893            gobject_ffi::g_value_set_flags(v.to_glib_none_mut().0, self.0.value);
894            v
895        }
896    }
897
898    // rustdoc-stripper-ignore-next
899    /// Convert flags values from a `Value`. This returns all flags that are set.
900    pub fn from_value(value: &Value) -> Option<(FlagsClass, Vec<&FlagsValue>)> {
901        unsafe {
902            let flags_class = FlagsClass::with_type(value.type_())?;
903            let mut res = Vec::new();
904            let f = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
905            for v in flags_class.values() {
906                if v.value() & f != 0 {
907                    res.push(&*(v as *const FlagsValue));
908                }
909            }
910            Some((flags_class, res))
911        }
912    }
913}
914
915impl PartialEq for FlagsValue {
916    fn eq(&self, other: &Self) -> bool {
917        self.value().eq(&other.value())
918    }
919}
920
921impl Eq for FlagsValue {}
922
923impl UnsafeFrom<gobject_ffi::GFlagsValue> for FlagsValue {
924    unsafe fn unsafe_from(g_value: gobject_ffi::GFlagsValue) -> Self {
925        unsafe { Self::unsafe_from(g_value) }
926    }
927}
928
929// rustdoc-stripper-ignore-next
930/// Define the zero value and the associated GLib type.
931impl EnumerationValue<FlagsValue> for FlagsValue {
932    type GlibType = gobject_ffi::GFlagsValue;
933    const ZERO: FlagsValue = unsafe {
934        FlagsValue::unsafe_from(gobject_ffi::GFlagsValue {
935            value: 0,
936            value_name: ptr::null(),
937            value_nick: ptr::null(),
938        })
939    };
940}
941
942// rustdoc-stripper-ignore-next
943/// Storage of flags values.
944pub type FlagsValuesStorage<const N: usize> = EnumerationValuesStorage<FlagsValue, N>;
945
946// rustdoc-stripper-ignore-next
947/// Representation of flags values wrapped by `FlagsValuesStorage`
948pub type FlagsValues = EnumerationValues<FlagsValue>;
949
950// rustdoc-stripper-ignore-next
951/// Builder for conveniently setting/unsetting flags and returning a `Value`.
952///
953/// Example for getting a flags property, unsetting some flags and setting the updated flags on the
954/// object again:
955///
956/// ```ignore
957/// let flags = obj.property("flags").unwrap();
958/// let flags_class = FlagsClass::new(flags.type_()).unwrap();
959/// let flags = flags_class.builder_with_value(flags).unwrap()
960///     .unset_by_nick("some-flag")
961///     .unset_by_nick("some-other-flag")
962///     .build()
963///     .unwrap();
964/// obj.set_property("flags", &flags).unwrap();
965/// ```
966///
967/// If setting/unsetting any value fails, `build()` returns `None`.
968#[must_use = "The builder must be built to be used"]
969pub struct FlagsBuilder<'a>(&'a FlagsClass, Option<Value>);
970impl FlagsBuilder<'_> {
971    fn new(flags_class: &FlagsClass) -> FlagsBuilder<'_> {
972        let value = unsafe { Value::from_type_unchecked(flags_class.type_()) };
973        FlagsBuilder(flags_class, Some(value))
974    }
975
976    fn with_value(flags_class: &FlagsClass, value: Value) -> FlagsBuilder<'_> {
977        FlagsBuilder(flags_class, Some(value))
978    }
979
980    // rustdoc-stripper-ignore-next
981    /// Set flags corresponding to integer value `f`.
982    pub fn set(mut self, f: u32) -> Self {
983        if let Some(value) = self.1.take() {
984            self.1 = self.0.set(value, f).ok();
985        }
986
987        self
988    }
989
990    // rustdoc-stripper-ignore-next
991    /// Set flags corresponding to string name `name`.
992    pub fn set_by_name(mut self, name: &str) -> Self {
993        if let Some(value) = self.1.take() {
994            self.1 = self.0.set_by_name(value, name).ok();
995        }
996
997        self
998    }
999
1000    // rustdoc-stripper-ignore-next
1001    /// Set flags corresponding to string nick `nick`.
1002    pub fn set_by_nick(mut self, nick: &str) -> Self {
1003        if let Some(value) = self.1.take() {
1004            self.1 = self.0.set_by_nick(value, nick).ok();
1005        }
1006
1007        self
1008    }
1009
1010    // rustdoc-stripper-ignore-next
1011    /// Unsets flags corresponding to integer value `f`.
1012    pub fn unset(mut self, f: u32) -> Self {
1013        if let Some(value) = self.1.take() {
1014            self.1 = self.0.unset(value, f).ok();
1015        }
1016
1017        self
1018    }
1019
1020    // rustdoc-stripper-ignore-next
1021    /// Unset flags corresponding to string name `name`.
1022    pub fn unset_by_name(mut self, name: &str) -> Self {
1023        if let Some(value) = self.1.take() {
1024            self.1 = self.0.unset_by_name(value, name).ok();
1025        }
1026
1027        self
1028    }
1029
1030    // rustdoc-stripper-ignore-next
1031    /// Unset flags corresponding to string nick `nick`.
1032    pub fn unset_by_nick(mut self, nick: &str) -> Self {
1033        if let Some(value) = self.1.take() {
1034            self.1 = self.0.unset_by_nick(value, nick).ok();
1035        }
1036
1037        self
1038    }
1039
1040    // rustdoc-stripper-ignore-next
1041    /// Converts to the final `Value`, unless any previous setting/unsetting of flags failed.
1042    #[must_use = "Value returned from the builder should probably be used"]
1043    pub fn build(self) -> Option<Value> {
1044        self.1
1045    }
1046}
1047
1048unsafe impl<'a> crate::value::FromValue<'a> for Vec<&FlagsValue> {
1049    type Checker = FlagsTypeChecker;
1050
1051    unsafe fn from_value(value: &'a Value) -> Self {
1052        unsafe {
1053            let (_, v) = FlagsValue::from_value(value).unwrap();
1054            // SAFETY: The enum class and its values live forever
1055            std::mem::transmute(v)
1056        }
1057    }
1058}
1059
1060pub struct FlagsTypeChecker();
1061unsafe impl crate::value::ValueTypeChecker for FlagsTypeChecker {
1062    type Error = InvalidFlagsError;
1063
1064    fn check(value: &Value) -> Result<(), Self::Error> {
1065        let t = value.type_();
1066        if t.is_a(Type::FLAGS) {
1067            Ok(())
1068        } else {
1069            Err(InvalidFlagsError)
1070        }
1071    }
1072}
1073
1074// rustdoc-stripper-ignore-next
1075/// An error returned from the [`get`](struct.Value.html#method.get) function
1076/// on a [`Value`](struct.Value.html) for flags types.
1077#[derive(Clone, PartialEq, Eq, Debug)]
1078pub struct InvalidFlagsError;
1079
1080impl fmt::Display for InvalidFlagsError {
1081    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1082        write!(f, "Value is not a flags")
1083    }
1084}
1085
1086impl std::error::Error for InvalidFlagsError {}
1087
1088// rustdoc-stripper-ignore-next
1089/// helper trait to define the zero value and the associated GLib type.
1090pub trait EnumerationValue<E>: Copy {
1091    type GlibType;
1092    const ZERO: E;
1093}
1094
1095// rustdoc-stripper-ignore-next
1096/// Storage of enumeration values terminated by a zero value. Should be used
1097/// only as a storage location for `EnumValue` or `FlagsValue` when registering
1098/// an enum or flags as a dynamic type.
1099/// see `TypePluginRegisterImpl::register_dynamic_enum()`, `TypePluginRegisterImpl::register_dynamic_flags()`
1100/// and `TypePluginImpl::complete_type_info()`.
1101/// Inner is intentionally private to ensure other modules will not access the
1102/// enum (or flags) values by this way.
1103/// Use `EnumClass::values()` or `EnumClass::value()` to get the enum values.
1104/// Use `FlagsClass::values()` or `FlagsClass::value()` to get the flags values.
1105#[repr(C)]
1106pub struct EnumerationValuesStorage<E: EnumerationValue<E>, const S: usize>([E; S]);
1107
1108impl<E: EnumerationValue<E>, const S: usize> EnumerationValuesStorage<E, S> {
1109    // rustdoc-stripper-ignore-next
1110    /// creates a new `EnumerationValuesStorage` with the given values and a final zero value.
1111    pub const fn new<const N: usize>(values: [E; N]) -> Self {
1112        #[repr(C)]
1113        #[derive(Copy, Clone)]
1114        struct Both<E: Copy, const N: usize>([E; N], [E; 1]);
1115
1116        #[repr(C)]
1117        union Transmute<E: Copy, const N: usize, const S: usize> {
1118            from: Both<E, N>,
1119            to: [E; S],
1120        }
1121
1122        // SAFETY: Transmute is repr(C) and union fields are compatible in terms of size and alignment, so the access to union fields is safe.
1123        unsafe {
1124            // create an array with the values and terminated by a zero value.
1125            let all = Transmute {
1126                from: Both(values, [E::ZERO; 1]),
1127            }
1128            .to;
1129            Self(all)
1130        }
1131    }
1132}
1133
1134impl<E: EnumerationValue<E>, const S: usize> AsRef<EnumerationValues<E>>
1135    for EnumerationValuesStorage<E, S>
1136{
1137    fn as_ref(&self) -> &EnumerationValues<E> {
1138        // SAFETY: EnumerationStorage and EnumerationValues are repr(C) and their unique field are compatible (array and slice of the same type), so the cast is safe.
1139        unsafe { &*(&self.0 as *const [E] as *const EnumerationValues<E>) }
1140    }
1141}
1142
1143// rustdoc-stripper-ignore-next
1144/// Representation of enumeration values wrapped by `EnumerationValuesStorage`.
1145/// Easier to use because don't have a size parameter to be specify. Should be
1146/// used only to register an enum or flags as a dynamic type.
1147/// see `TypePluginRegisterImpl::register_dynamic_enum()`, `TypePluginRegisterImpl::register_dynamic_flags()`
1148/// and `TypePluginImpl::complete_type_info()`.
1149/// Field is intentionally private to ensure other modules will not access the
1150/// enum (or flags) values by this way.
1151/// Use `EnumClass::values()` or `EnumClass::value()` to get the enum values.
1152/// Use `FlagsClass::values()` or `FlagsClass::value()` to get the flags values.
1153#[repr(C)]
1154pub struct EnumerationValues<E: EnumerationValue<E>>([E]);
1155
1156impl<E: EnumerationValue<E>> Deref for EnumerationValues<E> {
1157    type Target = [E];
1158
1159    // rustdoc-stripper-ignore-next
1160    /// Dereferences the enumeration values as a slice, but excluding the last value which is zero.
1161    fn deref(&self) -> &Self::Target {
1162        // SAFETY: EnumerationValues contains at least the zero value which terminates the array.
1163        unsafe { std::slice::from_raw_parts(self.0.as_ptr(), self.0.len() - 1) }
1164    }
1165}
1166
1167#[doc(hidden)]
1168impl<'a, E: 'a + EnumerationValue<E>> ToGlibPtr<'a, *const E::GlibType> for EnumerationValues<E> {
1169    type Storage = &'a Self;
1170
1171    fn to_glib_none(&'a self) -> Stash<'a, *const E::GlibType, Self> {
1172        Stash(self.0.as_ptr() as *const E::GlibType, self)
1173    }
1174}
1175
1176#[cfg(test)]
1177mod tests {
1178    use super::*;
1179
1180    #[test]
1181    fn test_flags() {
1182        let flags = FlagsClass::new::<crate::BindingFlags>();
1183        let values = flags.values();
1184        let def1 = values
1185            .iter()
1186            .find(|v| v.name() == "G_BINDING_DEFAULT")
1187            .unwrap();
1188        let def2 = flags.value_by_name("G_BINDING_DEFAULT").unwrap();
1189        assert!(ptr::eq(def1, def2));
1190
1191        let value = flags.to_value(0).unwrap();
1192        let values = value.get::<Vec<&FlagsValue>>().unwrap();
1193        assert_eq!(values.len(), 0);
1194
1195        assert_eq!(def1.value(), crate::BindingFlags::DEFAULT.bits());
1196    }
1197}