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 
41 use glib_sys;
42 use gobject_sys;
43 use gstring::GString;
44 use std::borrow::Cow;
45 use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
46 use std::fmt;
47 use std::hash::{Hash, Hasher};
48 use std::slice;
49 use std::str;
50 use translate::*;
51 use value;
52 use StaticType;
53 use Type;
54 use Value;
55 use VariantTy;
56 
57 glib_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 
69 impl StaticType for Variant {
static_type() -> Type70     fn static_type() -> Type {
71         Type::Variant
72     }
73 }
74 
75 #[doc(hidden)]
76 impl<'a> value::FromValueOptional<'a> for Variant {
from_value_optional(value: &Value) -> Option<Self>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)]
85 impl value::SetValue for Variant {
set_value(value: &mut Value, this: &Self)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)]
95 impl value::SetValueOptional for Variant {
set_value_optional(value: &mut Value, this: Option<&Self>)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 
104 impl Variant {
105     /// Returns the type of the value.
type_(&self) -> &VariantTy106     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]
is<T: StaticVariantType>(&self) -> bool112     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]
get<T: FromVariant>(&self) -> Option<T>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).
get_str(&self) -> Option<&str>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 
146 unsafe impl Send for Variant {}
147 unsafe impl Sync for Variant {}
148 
149 impl fmt::Debug for Variant {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result150     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 
159 impl fmt::Display for Variant {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result160     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 
171 impl PartialEq for Variant {
eq(&self, other: &Self) -> bool172     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 
182 impl Eq for Variant {}
183 
184 impl PartialOrd for Variant {
partial_cmp(&self, other: &Self) -> Option<Ordering>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 
212 impl Hash for Variant {
hash<H: Hasher>(&self, state: &mut H)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`.
219 pub trait ToVariant {
220     /// Returns a `Variant` clone of `self`.
to_variant(&self) -> Variant221     fn to_variant(&self) -> Variant;
222 }
223 
224 /// Extracts a value.
225 pub trait FromVariant: Sized + StaticVariantType {
226     /// Tries to extract a value.
227     ///
228     /// Returns `Some` if the variant's type matches `Self`.
from_variant(variant: &Variant) -> Option<Self>229     fn from_variant(variant: &Variant) -> Option<Self>;
230 }
231 
232 /// Returns `VariantType` of `Self`.
233 pub trait StaticVariantType {
234     /// Returns the `VariantType` corresponding to `Self`.
static_variant_type() -> Cow<'static, VariantTy>235     fn static_variant_type() -> Cow<'static, VariantTy>;
236 }
237 
238 impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T {
to_variant(&self) -> Variant239     fn to_variant(&self) -> Variant {
240         <T as ToVariant>::to_variant(self)
241     }
242 }
243 
244 impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T {
static_variant_type() -> Cow<'static, VariantTy>245     fn static_variant_type() -> Cow<'static, VariantTy> {
246         <T as StaticVariantType>::static_variant_type()
247     }
248 }
249 
250 macro_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 
278 impl_numeric!(u8, "y", g_variant_new_byte, g_variant_get_byte);
279 impl_numeric!(i16, "n", g_variant_new_int16, g_variant_get_int16);
280 impl_numeric!(u16, "q", g_variant_new_uint16, g_variant_get_uint16);
281 impl_numeric!(i32, "i", g_variant_new_int32, g_variant_get_int32);
282 impl_numeric!(u32, "u", g_variant_new_uint32, g_variant_get_uint32);
283 impl_numeric!(i64, "x", g_variant_new_int64, g_variant_get_int64);
284 impl_numeric!(u64, "t", g_variant_new_uint64, g_variant_get_uint64);
285 impl_numeric!(f64, "d", g_variant_new_double, g_variant_get_double);
286 
287 impl StaticVariantType for bool {
static_variant_type() -> Cow<'static, VariantTy>288     fn static_variant_type() -> Cow<'static, VariantTy> {
289         unsafe { VariantTy::from_str_unchecked("b").into() }
290     }
291 }
292 
293 impl ToVariant for bool {
to_variant(&self) -> Variant294     fn to_variant(&self) -> Variant {
295         unsafe { from_glib_none(glib_sys::g_variant_new_boolean(self.to_glib())) }
296     }
297 }
298 
299 impl FromVariant for bool {
from_variant(variant: &Variant) -> Option<Self>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 
313 impl StaticVariantType for String {
static_variant_type() -> Cow<'static, VariantTy>314     fn static_variant_type() -> Cow<'static, VariantTy> {
315         unsafe { VariantTy::from_str_unchecked("s").into() }
316     }
317 }
318 
319 impl ToVariant for String {
to_variant(&self) -> Variant320     fn to_variant(&self) -> Variant {
321         self[..].to_variant()
322     }
323 }
324 
325 impl FromVariant for String {
from_variant(variant: &Variant) -> Option<Self>326     fn from_variant(variant: &Variant) -> Option<Self> {
327         variant.get_str().map(String::from)
328     }
329 }
330 
331 impl StaticVariantType for str {
static_variant_type() -> Cow<'static, VariantTy>332     fn static_variant_type() -> Cow<'static, VariantTy> {
333         unsafe { VariantTy::from_str_unchecked("s").into() }
334     }
335 }
336 
337 impl ToVariant for str {
to_variant(&self) -> Variant338     fn to_variant(&self) -> Variant {
339         unsafe { from_glib_none(glib_sys::g_variant_new_take_string(self.to_glib_full())) }
340     }
341 }
342 
343 impl<T: ToVariant> From<T> for Variant {
from(value: T) -> Variant344     fn from(value: T) -> Variant {
345         value.to_variant()
346     }
347 }
348 
349 #[cfg(test)]
350 mod 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]
test_str()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]
test_string()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]
test_eq()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]
test_hash()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 }
426