glib/
variant.rs

1// Copyright 2013-2016, The Gtk-rs Project Developers.
2// See the COPYRIGHT file at the top-level directory of this distribution.
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5//! `Variant` binding and helper traits.
6//!
7//! [`Variant`](struct.Variant.html) is an immutable dynamically-typed generic
8//! container. Its type and value are defined at construction and never change.
9//!
10//! `Variant` types are described by [`VariantType`](../struct.VariantType.html)
11//! "type strings".
12//!
13//! Although `GVariant` supports arbitrarily complex types, this binding is
14//! currently limited to the basic ones: `bool`, `u8`, `i16`, `u16`, `i32`,
15//! `u32`, `i64`, `u64`, `f64` and `&str`/`String`.
16//!
17//! # Examples
18//!
19//! ```
20//! use glib::prelude::*; // or `use gtk::prelude::*;`
21//! use glib::Variant;
22//!
23//! // Using the `ToVariant` trait.
24//! let num = 10.to_variant();
25//!
26//! // `is` tests the type of the value.
27//! assert!(num.is::<i32>());
28//!
29//! // `get` tries to extract the value.
30//! assert_eq!(num.get::<i32>(), Some(10));
31//! assert_eq!(num.get::<u32>(), None);
32//!
33//! // `Variant` implements `From`
34//! let hello = Variant::from("Hello!");
35//!
36//! // `get_str` tries to borrow a string slice.
37//! assert_eq!(hello.get_str(), Some("Hello!"));
38//! assert_eq!(num.get_str(), None);
39//! ```
40
41use glib_sys;
42use gobject_sys;
43use gstring::GString;
44use std::borrow::Cow;
45use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
46use std::fmt;
47use std::hash::{Hash, Hasher};
48use std::slice;
49use std::str;
50use translate::*;
51use value;
52use StaticType;
53use Type;
54use Value;
55use VariantTy;
56
57glib_wrapper! {
58    /// A generic immutable value capable of carrying various types.
59    ///
60    /// See the [module documentation](index.html) for more details.
61    pub struct Variant(Shared<glib_sys::GVariant>);
62
63    match fn {
64        ref => |ptr| glib_sys::g_variant_ref_sink(ptr),
65        unref => |ptr| glib_sys::g_variant_unref(ptr),
66    }
67}
68
69impl StaticType for Variant {
70    fn static_type() -> Type {
71        Type::Variant
72    }
73}
74
75#[doc(hidden)]
76impl<'a> value::FromValueOptional<'a> for Variant {
77    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
78        from_glib_full(gobject_sys::g_value_dup_variant(
79            ToGlibPtr::to_glib_none(value).0,
80        ))
81    }
82}
83
84#[doc(hidden)]
85impl value::SetValue for Variant {
86    unsafe fn set_value(value: &mut Value, this: &Self) {
87        gobject_sys::g_value_set_variant(
88            ToGlibPtrMut::to_glib_none_mut(value).0,
89            ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(this).0,
90        )
91    }
92}
93
94#[doc(hidden)]
95impl value::SetValueOptional for Variant {
96    unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
97        gobject_sys::g_value_set_variant(
98            ToGlibPtrMut::to_glib_none_mut(value).0,
99            ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(&this).0,
100        )
101    }
102}
103
104impl Variant {
105    /// Returns the type of the value.
106    pub fn type_(&self) -> &VariantTy {
107        unsafe { VariantTy::from_ptr(glib_sys::g_variant_get_type(self.to_glib_none().0)) }
108    }
109
110    /// Returns `true` if the type of the value corresponds to `T`.
111    #[inline]
112    pub fn is<T: StaticVariantType>(&self) -> bool {
113        self.type_() == T::static_variant_type()
114    }
115
116    /// Tries to extract a value of type `T`.
117    ///
118    /// Returns `Some` if `T` matches the variant's type.
119    #[inline]
120    pub fn get<T: FromVariant>(&self) -> Option<T> {
121        T::from_variant(self)
122    }
123
124    /// Tries to extract a `&str`.
125    ///
126    /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type
127    /// strings).
128    pub fn get_str(&self) -> Option<&str> {
129        unsafe {
130            match self.type_().to_str() {
131                "s" | "o" | "g" => {
132                    let mut len = 0;
133                    let ptr = glib_sys::g_variant_get_string(self.to_glib_none().0, &mut len);
134                    let ret = str::from_utf8_unchecked(slice::from_raw_parts(
135                        ptr as *const u8,
136                        len as usize,
137                    ));
138                    Some(ret)
139                }
140                _ => None,
141            }
142        }
143    }
144}
145
146unsafe impl Send for Variant {}
147unsafe impl Sync for Variant {}
148
149impl fmt::Debug for Variant {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        f.debug_struct("Variant")
152            .field("ptr", &self.to_glib_none().0)
153            .field("type", &self.type_())
154            .field("value", &self.to_string())
155            .finish()
156    }
157}
158
159impl fmt::Display for Variant {
160    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161        let serialized: GString = unsafe {
162            from_glib_full(glib_sys::g_variant_print(
163                self.to_glib_none().0,
164                false.to_glib(),
165            ))
166        };
167        f.write_str(&serialized)
168    }
169}
170
171impl PartialEq for Variant {
172    fn eq(&self, other: &Self) -> bool {
173        unsafe {
174            from_glib(glib_sys::g_variant_equal(
175                self.to_glib_none().0 as *const _,
176                other.to_glib_none().0 as *const _,
177            ))
178        }
179    }
180}
181
182impl Eq for Variant {}
183
184impl PartialOrd for Variant {
185    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
186        unsafe {
187            if glib_sys::g_variant_classify(self.to_glib_none().0)
188                != glib_sys::g_variant_classify(other.to_glib_none().0)
189            {
190                return None;
191            }
192
193            if glib_sys::g_variant_is_container(self.to_glib_none().0) != glib_sys::GFALSE {
194                return None;
195            }
196
197            let res = glib_sys::g_variant_compare(
198                self.to_glib_none().0 as *const _,
199                other.to_glib_none().0 as *const _,
200            );
201            if res < 0 {
202                Some(Ordering::Less)
203            } else if res > 0 {
204                Some(Ordering::Equal)
205            } else {
206                Some(Ordering::Greater)
207            }
208        }
209    }
210}
211
212impl Hash for Variant {
213    fn hash<H: Hasher>(&self, state: &mut H) {
214        unsafe { state.write_u32(glib_sys::g_variant_hash(self.to_glib_none().0 as *const _)) }
215    }
216}
217
218/// Converts to `Variant`.
219pub trait ToVariant {
220    /// Returns a `Variant` clone of `self`.
221    fn to_variant(&self) -> Variant;
222}
223
224/// Extracts a value.
225pub trait FromVariant: Sized + StaticVariantType {
226    /// Tries to extract a value.
227    ///
228    /// Returns `Some` if the variant's type matches `Self`.
229    fn from_variant(variant: &Variant) -> Option<Self>;
230}
231
232/// Returns `VariantType` of `Self`.
233pub trait StaticVariantType {
234    /// Returns the `VariantType` corresponding to `Self`.
235    fn static_variant_type() -> Cow<'static, VariantTy>;
236}
237
238impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T {
239    fn to_variant(&self) -> Variant {
240        <T as ToVariant>::to_variant(self)
241    }
242}
243
244impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T {
245    fn static_variant_type() -> Cow<'static, VariantTy> {
246        <T as StaticVariantType>::static_variant_type()
247    }
248}
249
250macro_rules! impl_numeric {
251    ($name:ty, $type_str:expr, $new_fn:ident, $get_fn:ident) => {
252        impl StaticVariantType for $name {
253            fn static_variant_type() -> Cow<'static, VariantTy> {
254                unsafe { VariantTy::from_str_unchecked($type_str).into() }
255            }
256        }
257
258        impl ToVariant for $name {
259            fn to_variant(&self) -> Variant {
260                unsafe { from_glib_none(glib_sys::$new_fn(*self)) }
261            }
262        }
263
264        impl FromVariant for $name {
265            fn from_variant(variant: &Variant) -> Option<Self> {
266                unsafe {
267                    if variant.is::<Self>() {
268                        Some(glib_sys::$get_fn(variant.to_glib_none().0))
269                    } else {
270                        None
271                    }
272                }
273            }
274        }
275    };
276}
277
278impl_numeric!(u8, "y", g_variant_new_byte, g_variant_get_byte);
279impl_numeric!(i16, "n", g_variant_new_int16, g_variant_get_int16);
280impl_numeric!(u16, "q", g_variant_new_uint16, g_variant_get_uint16);
281impl_numeric!(i32, "i", g_variant_new_int32, g_variant_get_int32);
282impl_numeric!(u32, "u", g_variant_new_uint32, g_variant_get_uint32);
283impl_numeric!(i64, "x", g_variant_new_int64, g_variant_get_int64);
284impl_numeric!(u64, "t", g_variant_new_uint64, g_variant_get_uint64);
285impl_numeric!(f64, "d", g_variant_new_double, g_variant_get_double);
286
287impl StaticVariantType for bool {
288    fn static_variant_type() -> Cow<'static, VariantTy> {
289        unsafe { VariantTy::from_str_unchecked("b").into() }
290    }
291}
292
293impl ToVariant for bool {
294    fn to_variant(&self) -> Variant {
295        unsafe { from_glib_none(glib_sys::g_variant_new_boolean(self.to_glib())) }
296    }
297}
298
299impl FromVariant for bool {
300    fn from_variant(variant: &Variant) -> Option<Self> {
301        unsafe {
302            if variant.is::<Self>() {
303                Some(from_glib(glib_sys::g_variant_get_boolean(
304                    variant.to_glib_none().0,
305                )))
306            } else {
307                None
308            }
309        }
310    }
311}
312
313impl StaticVariantType for String {
314    fn static_variant_type() -> Cow<'static, VariantTy> {
315        unsafe { VariantTy::from_str_unchecked("s").into() }
316    }
317}
318
319impl ToVariant for String {
320    fn to_variant(&self) -> Variant {
321        self[..].to_variant()
322    }
323}
324
325impl FromVariant for String {
326    fn from_variant(variant: &Variant) -> Option<Self> {
327        variant.get_str().map(String::from)
328    }
329}
330
331impl StaticVariantType for str {
332    fn static_variant_type() -> Cow<'static, VariantTy> {
333        unsafe { VariantTy::from_str_unchecked("s").into() }
334    }
335}
336
337impl ToVariant for str {
338    fn to_variant(&self) -> Variant {
339        unsafe { from_glib_none(glib_sys::g_variant_new_take_string(self.to_glib_full())) }
340    }
341}
342
343impl<T: ToVariant> From<T> for Variant {
344    fn from(value: T) -> Variant {
345        value.to_variant()
346    }
347}
348
349#[cfg(test)]
350mod tests {
351    use super::*;
352    use std::collections::HashSet;
353
354    macro_rules! unsigned {
355        ($name:ident, $ty:ident) => {
356            #[test]
357            fn $name() {
358                let mut n = $ty::max_value();
359                while n > 0 {
360                    let v = Variant::from(n);
361                    assert_eq!(v.get(), Some(n));
362                    n /= 2;
363                }
364            }
365        };
366    }
367
368    macro_rules! signed {
369        ($name:ident, $ty:ident) => {
370            #[test]
371            fn $name() {
372                let mut n = $ty::max_value();
373                while n > 0 {
374                    let v = Variant::from(n);
375                    assert_eq!(v.get(), Some(n));
376                    let v = Variant::from(-n);
377                    assert_eq!(v.get(), Some(-n));
378                    n /= 2;
379                }
380            }
381        };
382    }
383
384    unsigned!(test_u8, u8);
385    unsigned!(test_u16, u16);
386    unsigned!(test_u32, u32);
387    unsigned!(test_u64, u64);
388    signed!(test_i16, i16);
389    signed!(test_i32, i32);
390    signed!(test_i64, i64);
391
392    #[test]
393    fn test_str() {
394        let s = "this is a test";
395        let v = Variant::from(s);
396        assert_eq!(v.get_str(), Some(s));
397    }
398
399    #[test]
400    fn test_string() {
401        let s = String::from("this is a test");
402        let v = Variant::from(s.clone());
403        assert_eq!(v.get(), Some(s.clone()));
404    }
405
406    #[test]
407    fn test_eq() {
408        let v1 = Variant::from("this is a test");
409        let v2 = Variant::from("this is a test");
410        let v3 = Variant::from("test");
411        assert_eq!(v1, v2);
412        assert!(v1 != v3);
413    }
414
415    #[test]
416    fn test_hash() {
417        let v1 = Variant::from("this is a test");
418        let v2 = Variant::from("this is a test");
419        let v3 = Variant::from("test");
420        let mut set = HashSet::new();
421        set.insert(v1);
422        assert!(set.contains(&v2));
423        assert!(!set.contains(&v3));
424    }
425}