1 // Take a look at the license at the top of the repository in the LICENSE file.
2
3 //! `Variant` binding and helper traits.
4 //!
5 //! [`Variant`](struct.Variant.html) is an immutable dynamically-typed generic
6 //! container. Its type and value are defined at construction and never change.
7 //!
8 //! `Variant` types are described by [`VariantType`](../struct.VariantType.html)
9 //! "type strings".
10 //!
11 //! Although `GVariant` supports arbitrarily complex types, this binding is
12 //! currently limited to the basic ones: `bool`, `u8`, `i16`, `u16`, `i32`,
13 //! `u32`, `i64`, `u64`, `f64`, `&str`/`String`, and [`VariantDict`](../struct.VariantDict.html).
14 //!
15 //! # Examples
16 //!
17 //! ```
18 //! use glib::prelude::*; // or `use gtk::prelude::*;`
19 //! use glib::{Variant, FromVariant, ToVariant};
20 //! use std::collections::HashMap;
21 //!
22 //! // Using the `ToVariant` trait.
23 //! let num = 10.to_variant();
24 //!
25 //! // `is` tests the type of the value.
26 //! assert!(num.is::<i32>());
27 //!
28 //! // `get` tries to extract the value.
29 //! assert_eq!(num.get::<i32>(), Some(10));
30 //! assert_eq!(num.get::<u32>(), None);
31 //!
32 //! // `get_str` tries to borrow a string slice.
33 //! let hello = "Hello!".to_variant();
34 //! assert_eq!(hello.str(), Some("Hello!"));
35 //! assert_eq!(num.str(), None);
36 //!
37 //! // `bytes` tries to borrow a byte array (GVariant type `ay`),
38 //! // rather than creating a deep copy which would be expensive for
39 //! // nontrivially sized byte arrays.
40 //! // The test data here is the zstd compression header, which
41 //! // stands in for arbitrary binary data (e.g. not UTF-8).
42 //! let bufdata = b"\xFD\x2F\xB5\x28";
43 //! let bufv = bufdata.to_variant();
44 //! assert_eq!(bufv.bytes().unwrap(), bufdata);
45 //! assert!(num.bytes().is_err());
46 //!
47 //! // Variant carrying a Variant
48 //! let variant = Variant::from_variant(&hello);
49 //! let variant = variant.as_variant().unwrap();
50 //! assert_eq!(variant.str(), Some("Hello!"));
51 //!
52 //! // Variant carrying an array
53 //! let array = ["Hello".to_variant(), "there!".to_variant()];
54 //! let variant = Variant::from_array::<&str>(&array);
55 //! assert_eq!(variant.n_children(), 2);
56 //! assert_eq!(variant.child_value(0).str(), Some("Hello"));
57 //! assert_eq!(variant.child_value(1).str(), Some("there!"));
58 //!
59 //! // You can also convert from and to a Vec
60 //! let array = vec!["Hello", "there!"].to_variant();
61 //! assert_eq!(variant.n_children(), 2);
62 //! let vec = <Vec<String>>::from_variant(&array).unwrap();
63 //! assert_eq!(vec[0], "Hello");
64 //!
65 //! // Conversion to and from HashMap is also possible
66 //! let mut map: HashMap<u16, &str> = HashMap::new();
67 //! map.insert(1, "hi");
68 //! map.insert(2, "there");
69 //! let variant = map.to_variant();
70 //! assert_eq!(variant.n_children(), 2);
71 //! let map: HashMap<u16, String> = HashMap::from_variant(&variant).unwrap();
72 //! assert_eq!(map[&1], "hi");
73 //! assert_eq!(map[&2], "there");
74 //!
75 //! // And conversion to and from tuples.
76 //! let variant = ("hello", 42u16, vec![ "there", "you" ],).to_variant();
77 //! assert_eq!(variant.n_children(), 3);
78 //! assert_eq!(variant.type_().to_str(), "(sqas)");
79 //! let tuple = <(String, u16, Vec<String>)>::from_variant(&variant).unwrap();
80 //! assert_eq!(tuple.0, "hello");
81 //! assert_eq!(tuple.1, 42);
82 //! assert_eq!(tuple.2, &[ "there", "you"]);
83 //!
84 //! // `Option` is supported as well, through maybe types
85 //! let variant = Some("hello").to_variant();
86 //! assert_eq!(variant.n_children(), 1);
87 //! let mut s = <Option<String>>::from_variant(&variant).unwrap();
88 //! assert_eq!(s.unwrap(), "hello");
89 //! s = None;
90 //! let variant = s.to_variant();
91 //! assert_eq!(variant.n_children(), 0);
92 //! let s = <Option<String>>::from_variant(&variant).unwrap();
93 //! assert!(s.is_none());
94 //! ```
95
96 use crate::bytes::Bytes;
97 use crate::gstring::GString;
98 use crate::translate::*;
99 use crate::StaticType;
100 use crate::Type;
101 use crate::VariantTy;
102 use crate::VariantType;
103 use crate::{VariantIter, VariantStrIter};
104 use std::borrow::Cow;
105 use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
106 use std::collections::HashMap;
107 use std::fmt;
108 use std::hash::{BuildHasher, Hash, Hasher};
109 use std::slice;
110 use std::str;
111
112 wrapper! {
113 /// A generic immutable value capable of carrying various types.
114 ///
115 /// See the [module documentation](index.html) for more details.
116 #[doc(alias = "GVariant")]
117 pub struct Variant(Shared<ffi::GVariant>);
118
119 match fn {
120 ref => |ptr| ffi::g_variant_ref_sink(ptr),
121 unref => |ptr| ffi::g_variant_unref(ptr),
122 }
123 }
124
125 impl StaticType for Variant {
static_type() -> Type126 fn static_type() -> Type {
127 Type::VARIANT
128 }
129 }
130
131 #[doc(hidden)]
132 impl crate::value::ValueType for Variant {
133 type Type = Variant;
134 }
135
136 #[doc(hidden)]
137 unsafe impl<'a> crate::value::FromValue<'a> for Variant {
138 type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>;
139
from_value(value: &'a crate::Value) -> Self140 unsafe fn from_value(value: &'a crate::Value) -> Self {
141 let ptr = gobject_ffi::g_value_dup_variant(value.to_glib_none().0);
142 assert!(!ptr.is_null());
143 from_glib_full(ptr)
144 }
145 }
146
147 #[doc(hidden)]
148 impl crate::value::ToValue for Variant {
to_value(&self) -> crate::Value149 fn to_value(&self) -> crate::Value {
150 unsafe {
151 let mut value = crate::Value::from_type(Variant::static_type());
152 gobject_ffi::g_value_take_variant(
153 value.to_glib_none_mut().0,
154 self.to_glib_full() as *mut _,
155 );
156 value
157 }
158 }
159
value_type(&self) -> crate::Type160 fn value_type(&self) -> crate::Type {
161 Variant::static_type()
162 }
163 }
164
165 #[doc(hidden)]
166 impl crate::value::ToValueOptional for Variant {
to_value_optional(s: Option<&Self>) -> crate::Value167 fn to_value_optional(s: Option<&Self>) -> crate::Value {
168 let mut value = crate::Value::for_value_type::<Self>();
169 unsafe {
170 gobject_ffi::g_value_take_variant(
171 value.to_glib_none_mut().0,
172 s.to_glib_full() as *mut _,
173 );
174 }
175
176 value
177 }
178 }
179
180 /// An error returned from the [`try_get`](struct.Variant.html#method.try_get) function
181 /// on a [`Variant`](struct.Variant.html) when the expected type does not match the actual type.
182 #[derive(Clone, PartialEq, Eq, Debug)]
183 pub struct VariantTypeMismatchError {
184 pub actual: VariantType,
185 pub expected: VariantType,
186 }
187
188 impl VariantTypeMismatchError {
new(actual: VariantType, expected: VariantType) -> Self189 pub fn new(actual: VariantType, expected: VariantType) -> Self {
190 Self { actual, expected }
191 }
192 }
193
194 impl fmt::Display for VariantTypeMismatchError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 write!(
197 f,
198 "Type mismatch: Expected '{}' got '{}'",
199 self.expected, self.actual
200 )
201 }
202 }
203
204 impl std::error::Error for VariantTypeMismatchError {}
205
206 impl Variant {
207 /// Returns the type of the value.
type_(&self) -> &VariantTy208 pub fn type_(&self) -> &VariantTy {
209 unsafe { VariantTy::from_ptr(ffi::g_variant_get_type(self.to_glib_none().0)) }
210 }
211
212 /// Returns `true` if the type of the value corresponds to `T`.
213 #[inline]
is<T: StaticVariantType>(&self) -> bool214 pub fn is<T: StaticVariantType>(&self) -> bool {
215 self.type_() == T::static_variant_type()
216 }
217
218 /// Tries to extract a value of type `T`.
219 ///
220 /// Returns `Some` if `T` matches the variant's type.
221 #[inline]
get<T: FromVariant>(&self) -> Option<T>222 pub fn get<T: FromVariant>(&self) -> Option<T> {
223 T::from_variant(self)
224 }
225
226 /// Tries to extract a value of type `T`.
try_get<T: FromVariant>(&self) -> Result<T, VariantTypeMismatchError>227 pub fn try_get<T: FromVariant>(&self) -> Result<T, VariantTypeMismatchError> {
228 self.get().ok_or_else(|| {
229 VariantTypeMismatchError::new(
230 self.type_().to_owned(),
231 T::static_variant_type().into_owned(),
232 )
233 })
234 }
235
236 /// Boxes value.
237 #[inline]
from_variant(value: &Variant) -> Self238 pub fn from_variant(value: &Variant) -> Self {
239 unsafe { from_glib_none(ffi::g_variant_new_variant(value.to_glib_none().0)) }
240 }
241
242 /// Unboxes self.
243 ///
244 /// Returns `Some` if self contains a `Variant`.
245 #[inline]
246 #[doc(alias = "get_variant")]
as_variant(&self) -> Option<Variant>247 pub fn as_variant(&self) -> Option<Variant> {
248 unsafe { from_glib_full(ffi::g_variant_get_variant(self.to_glib_none().0)) }
249 }
250
251 /// Reads a child item out of a container `Variant` instance.
252 ///
253 /// # Panics
254 ///
255 /// * if `self` is not a container type.
256 /// * if given `index` is larger than number of children.
257 #[doc(alias = "get_child_value")]
258 #[doc(alias = "g_variant_get_child_value")]
child_value(&self, index: usize) -> Variant259 pub fn child_value(&self, index: usize) -> Variant {
260 assert!(self.is_container());
261 assert!(index < self.n_children());
262
263 unsafe { from_glib_full(ffi::g_variant_get_child_value(self.to_glib_none().0, index)) }
264 }
265
266 /// Try to read a child item out of a container `Variant` instance.
267 ///
268 /// It returns `None` if `self` is not a container type or if the given
269 /// `index` is larger than number of children.
try_child_value(&self, index: usize) -> Option<Variant>270 pub fn try_child_value(&self, index: usize) -> Option<Variant> {
271 if !(self.is_container() && index < self.n_children()) {
272 return None;
273 }
274
275 let v =
276 unsafe { from_glib_full(ffi::g_variant_get_child_value(self.to_glib_none().0, index)) };
277 Some(v)
278 }
279
280 /// Try to read a child item out of a container `Variant` instance.
281 ///
282 /// It returns `Ok(None)` if `self` is not a container type or if the given
283 /// `index` is larger than number of children. An error is thrown if the
284 /// type does not match.
try_child_get<T: StaticVariantType + FromVariant>( &self, index: usize, ) -> Result<Option<T>, VariantTypeMismatchError>285 pub fn try_child_get<T: StaticVariantType + FromVariant>(
286 &self,
287 index: usize,
288 ) -> Result<Option<T>, VariantTypeMismatchError> {
289 // TODO: In the future optimize this by using g_variant_get_child()
290 // directly to avoid allocating a GVariant.
291 self.try_child_value(index).map(|v| v.try_get()).transpose()
292 }
293
294 /// Read a child item out of a container `Variant` instance.
295 ///
296 /// # Panics
297 ///
298 /// * if `self` is not a container type.
299 /// * if given `index` is larger than number of children.
300 /// * if the expected variant type does not match
child_get<T: StaticVariantType + FromVariant>(&self, index: usize) -> T301 pub fn child_get<T: StaticVariantType + FromVariant>(&self, index: usize) -> T {
302 // TODO: In the future optimize this by using g_variant_get_child()
303 // directly to avoid allocating a GVariant.
304 self.child_value(index).get().unwrap()
305 }
306
307 /// Tries to extract a `&str`.
308 ///
309 /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type
310 /// strings).
311 #[doc(alias = "get_str")]
312 #[doc(alias = "g_variant_get_string")]
str(&self) -> Option<&str>313 pub fn str(&self) -> Option<&str> {
314 unsafe {
315 match self.type_().to_str() {
316 "s" | "o" | "g" => {
317 let mut len = 0;
318 let ptr = ffi::g_variant_get_string(self.to_glib_none().0, &mut len);
319 let ret = str::from_utf8_unchecked(slice::from_raw_parts(
320 ptr as *const u8,
321 len as usize,
322 ));
323 Some(ret)
324 }
325 _ => None,
326 }
327 }
328 }
329
330 /// Tries to extract a `&[u8]` from a variant of type `ay` (array of bytes).
331 ///
332 /// Returns an error if the type is not `ay`.
bytes(&self) -> Result<&[u8], VariantTypeMismatchError>333 pub fn bytes(&self) -> Result<&[u8], VariantTypeMismatchError> {
334 unsafe {
335 let t = self.type_();
336 let expected_ty = &*Vec::<u8>::static_variant_type();
337 if t == expected_ty {
338 let selfv = self.to_glib_none();
339 let len = ffi::g_variant_get_size(selfv.0);
340 let ptr = ffi::g_variant_get_data(selfv.0);
341 let ret = slice::from_raw_parts(ptr as *const u8, len as usize);
342 Ok(ret)
343 } else {
344 Err(VariantTypeMismatchError {
345 actual: t.to_owned(),
346 expected: expected_ty.to_owned(),
347 })
348 }
349 }
350 }
351
352 /// Creates a new GVariant array from children.
353 ///
354 /// All children must be of type `T`.
from_array<T: StaticVariantType>(children: &[Variant]) -> Self355 pub fn from_array<T: StaticVariantType>(children: &[Variant]) -> Self {
356 let type_ = T::static_variant_type();
357
358 for child in children {
359 assert_eq!(type_, child.type_());
360 }
361 unsafe {
362 from_glib_none(ffi::g_variant_new_array(
363 type_.as_ptr() as *const _,
364 children.to_glib_none().0,
365 children.len(),
366 ))
367 }
368 }
369
370 /// Creates a new GVariant tuple from children.
from_tuple(children: &[Variant]) -> Self371 pub fn from_tuple(children: &[Variant]) -> Self {
372 unsafe {
373 from_glib_none(ffi::g_variant_new_tuple(
374 children.to_glib_none().0,
375 children.len(),
376 ))
377 }
378 }
379
380 /// Creates a new maybe Variant.
from_maybe<T: StaticVariantType>(child: Option<&Variant>) -> Self381 pub fn from_maybe<T: StaticVariantType>(child: Option<&Variant>) -> Self {
382 let type_ = T::static_variant_type();
383 let ptr = match child {
384 Some(child) => {
385 assert_eq!(type_, child.type_());
386
387 child.to_glib_none().0
388 }
389 None => std::ptr::null(),
390 };
391 unsafe {
392 from_glib_none(ffi::g_variant_new_maybe(
393 type_.as_ptr() as *const _,
394 ptr as *mut ffi::GVariant,
395 ))
396 }
397 }
398
399 /// Constructs a new serialised-mode GVariant instance.
400 #[doc(alias = "g_variant_new_from_bytes")]
from_bytes<T: StaticVariantType>(bytes: &Bytes) -> Self401 pub fn from_bytes<T: StaticVariantType>(bytes: &Bytes) -> Self {
402 unsafe {
403 from_glib_none(ffi::g_variant_new_from_bytes(
404 T::static_variant_type().as_ptr() as *const _,
405 bytes.to_glib_none().0,
406 false.into_glib(),
407 ))
408 }
409 }
410
411 /// Constructs a new serialised-mode GVariant instance.
412 ///
413 /// This is the same as `from_bytes`, except that checks on the passed
414 /// data are skipped.
415 ///
416 /// You should not use this function on data from external sources.
417 ///
418 /// # Safety
419 ///
420 /// Since the data is not validated, this is potentially dangerous if called
421 /// on bytes which are not guaranteed to have come from serialising another
422 /// Variant. The caller is responsible for ensuring bad data is not passed in.
from_bytes_trusted<T: StaticVariantType>(bytes: &Bytes) -> Self423 pub unsafe fn from_bytes_trusted<T: StaticVariantType>(bytes: &Bytes) -> Self {
424 from_glib_none(ffi::g_variant_new_from_bytes(
425 T::static_variant_type().as_ptr() as *const _,
426 bytes.to_glib_none().0,
427 true.into_glib(),
428 ))
429 }
430
431 /// Returns the serialised form of a GVariant instance.
432 #[doc(alias = "get_data_as_bytes")]
433 #[doc(alias = "g_variant_get_data_as_bytes")]
data_as_bytes(&self) -> Bytes434 pub fn data_as_bytes(&self) -> Bytes {
435 unsafe { from_glib_full(ffi::g_variant_get_data_as_bytes(self.to_glib_none().0)) }
436 }
437
438 /// Returns a copy of the variant in normal form.
439 #[doc(alias = "g_variant_get_normal_form")]
normal_form(&self) -> Self440 pub fn normal_form(&self) -> Self {
441 unsafe { from_glib_full(ffi::g_variant_get_normal_form(self.to_glib_none().0)) }
442 }
443
444 /// Returns a copy of the variant in the opposite endianness.
445 #[doc(alias = "g_variant_byteswap")]
byteswap(&self) -> Self446 pub fn byteswap(&self) -> Self {
447 unsafe { from_glib_full(ffi::g_variant_byteswap(self.to_glib_none().0)) }
448 }
449
450 /// Determines the number of children in a container GVariant instance.
451 #[doc(alias = "g_variant_n_children")]
n_children(&self) -> usize452 pub fn n_children(&self) -> usize {
453 assert!(self.is_container());
454
455 unsafe { ffi::g_variant_n_children(self.to_glib_none().0) }
456 }
457
458 /// Create an iterator over items in the variant.
459 ///
460 /// Note that this heap allocates a variant for each element,
461 /// which can be particularly expensive for large arrays.
iter(&self) -> VariantIter462 pub fn iter(&self) -> VariantIter {
463 assert!(self.is_container());
464
465 VariantIter::new(self.clone())
466 }
467
468 /// Create an iterator over borrowed strings from a GVariant of type `as` (array of string).
469 ///
470 /// This will fail if the variant is not an array of with
471 /// the expected child type.
472 ///
473 /// A benefit of this API over [`Self::iter()`] is that it
474 /// minimizes allocation, and provides strongly typed access.
475 ///
476 /// ```
477 /// # use glib::prelude::*;
478 /// let strs = &["foo", "bar"];
479 /// let strs_variant: glib::Variant = strs.to_variant();
480 /// for s in strs_variant.array_iter_str()? {
481 /// println!("{}", s);
482 /// }
483 /// # Ok::<(), Box<dyn std::error::Error>>(())
484 /// ```
array_iter_str(&self) -> Result<VariantStrIter, VariantTypeMismatchError>485 pub fn array_iter_str(&self) -> Result<VariantStrIter, VariantTypeMismatchError> {
486 let child_ty = String::static_variant_type();
487 let actual_ty = self.type_();
488 let expected_ty = child_ty.with_array();
489 if actual_ty != expected_ty {
490 return Err(VariantTypeMismatchError {
491 actual: actual_ty.to_owned(),
492 expected: expected_ty,
493 });
494 }
495
496 Ok(VariantStrIter::new(self))
497 }
498
499 /// Variant has a container type.
500 #[doc(alias = "g_variant_is_container")]
is_container(&self) -> bool501 pub fn is_container(&self) -> bool {
502 unsafe { ffi::g_variant_is_container(self.to_glib_none().0) != ffi::GFALSE }
503 }
504 }
505
506 unsafe impl Send for Variant {}
507 unsafe impl Sync for Variant {}
508
509 impl fmt::Debug for Variant {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result510 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
511 f.debug_struct("Variant")
512 .field("ptr", &self.to_glib_none().0)
513 .field("type", &self.type_())
514 .field("value", &self.to_string())
515 .finish()
516 }
517 }
518
519 impl fmt::Display for Variant {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result520 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
521 let serialized: GString = unsafe {
522 from_glib_full(ffi::g_variant_print(
523 self.to_glib_none().0,
524 false.into_glib(),
525 ))
526 };
527 f.write_str(&serialized)
528 }
529 }
530
531 impl PartialEq for Variant {
532 #[doc(alias = "g_variant_equal")]
eq(&self, other: &Self) -> bool533 fn eq(&self, other: &Self) -> bool {
534 unsafe {
535 from_glib(ffi::g_variant_equal(
536 self.to_glib_none().0 as *const _,
537 other.to_glib_none().0 as *const _,
538 ))
539 }
540 }
541 }
542
543 impl Eq for Variant {}
544
545 impl PartialOrd for Variant {
partial_cmp(&self, other: &Self) -> Option<Ordering>546 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
547 unsafe {
548 if ffi::g_variant_classify(self.to_glib_none().0)
549 != ffi::g_variant_classify(other.to_glib_none().0)
550 {
551 return None;
552 }
553
554 if self.is_container() {
555 return None;
556 }
557
558 let res = ffi::g_variant_compare(
559 self.to_glib_none().0 as *const _,
560 other.to_glib_none().0 as *const _,
561 );
562
563 Some(res.cmp(&0))
564 }
565 }
566 }
567
568 impl Hash for Variant {
569 #[doc(alias = "g_variant_hash")]
hash<H: Hasher>(&self, state: &mut H)570 fn hash<H: Hasher>(&self, state: &mut H) {
571 unsafe { state.write_u32(ffi::g_variant_hash(self.to_glib_none().0 as *const _)) }
572 }
573 }
574
575 /// Converts to `Variant`.
576 pub trait ToVariant {
577 /// Returns a `Variant` clone of `self`.
to_variant(&self) -> Variant578 fn to_variant(&self) -> Variant;
579 }
580
581 /// Extracts a value.
582 pub trait FromVariant: Sized + StaticVariantType {
583 /// Tries to extract a value.
584 ///
585 /// Returns `Some` if the variant's type matches `Self`.
from_variant(variant: &Variant) -> Option<Self>586 fn from_variant(variant: &Variant) -> Option<Self>;
587 }
588
589 /// Returns `VariantType` of `Self`.
590 pub trait StaticVariantType {
591 /// Returns the `VariantType` corresponding to `Self`.
static_variant_type() -> Cow<'static, VariantTy>592 fn static_variant_type() -> Cow<'static, VariantTy>;
593 }
594
595 impl StaticVariantType for Variant {
static_variant_type() -> Cow<'static, VariantTy>596 fn static_variant_type() -> Cow<'static, VariantTy> {
597 unsafe { VariantTy::from_str_unchecked("v").into() }
598 }
599 }
600
601 impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T {
to_variant(&self) -> Variant602 fn to_variant(&self) -> Variant {
603 <T as ToVariant>::to_variant(self)
604 }
605 }
606
607 impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T {
static_variant_type() -> Cow<'static, VariantTy>608 fn static_variant_type() -> Cow<'static, VariantTy> {
609 <T as StaticVariantType>::static_variant_type()
610 }
611 }
612
613 macro_rules! impl_numeric {
614 ($name:ty, $type_str:expr, $new_fn:ident, $get_fn:ident) => {
615 impl StaticVariantType for $name {
616 fn static_variant_type() -> Cow<'static, VariantTy> {
617 unsafe { VariantTy::from_str_unchecked($type_str).into() }
618 }
619 }
620
621 impl ToVariant for $name {
622 fn to_variant(&self) -> Variant {
623 unsafe { from_glib_none(ffi::$new_fn(*self)) }
624 }
625 }
626
627 impl FromVariant for $name {
628 fn from_variant(variant: &Variant) -> Option<Self> {
629 unsafe {
630 if variant.is::<Self>() {
631 Some(ffi::$get_fn(variant.to_glib_none().0))
632 } else {
633 None
634 }
635 }
636 }
637 }
638 };
639 }
640
641 impl_numeric!(u8, "y", g_variant_new_byte, g_variant_get_byte);
642 impl_numeric!(i16, "n", g_variant_new_int16, g_variant_get_int16);
643 impl_numeric!(u16, "q", g_variant_new_uint16, g_variant_get_uint16);
644 impl_numeric!(i32, "i", g_variant_new_int32, g_variant_get_int32);
645 impl_numeric!(u32, "u", g_variant_new_uint32, g_variant_get_uint32);
646 impl_numeric!(i64, "x", g_variant_new_int64, g_variant_get_int64);
647 impl_numeric!(u64, "t", g_variant_new_uint64, g_variant_get_uint64);
648 impl_numeric!(f64, "d", g_variant_new_double, g_variant_get_double);
649
650 impl StaticVariantType for bool {
static_variant_type() -> Cow<'static, VariantTy>651 fn static_variant_type() -> Cow<'static, VariantTy> {
652 unsafe { VariantTy::from_str_unchecked("b").into() }
653 }
654 }
655
656 impl ToVariant for bool {
to_variant(&self) -> Variant657 fn to_variant(&self) -> Variant {
658 unsafe { from_glib_none(ffi::g_variant_new_boolean(self.into_glib())) }
659 }
660 }
661
662 impl FromVariant for bool {
from_variant(variant: &Variant) -> Option<Self>663 fn from_variant(variant: &Variant) -> Option<Self> {
664 unsafe {
665 if variant.is::<Self>() {
666 Some(from_glib(ffi::g_variant_get_boolean(
667 variant.to_glib_none().0,
668 )))
669 } else {
670 None
671 }
672 }
673 }
674 }
675
676 impl StaticVariantType for String {
static_variant_type() -> Cow<'static, VariantTy>677 fn static_variant_type() -> Cow<'static, VariantTy> {
678 unsafe { VariantTy::from_str_unchecked("s").into() }
679 }
680 }
681
682 impl ToVariant for String {
to_variant(&self) -> Variant683 fn to_variant(&self) -> Variant {
684 self[..].to_variant()
685 }
686 }
687
688 impl FromVariant for String {
from_variant(variant: &Variant) -> Option<Self>689 fn from_variant(variant: &Variant) -> Option<Self> {
690 variant.str().map(String::from)
691 }
692 }
693
694 impl StaticVariantType for str {
static_variant_type() -> Cow<'static, VariantTy>695 fn static_variant_type() -> Cow<'static, VariantTy> {
696 unsafe { VariantTy::from_str_unchecked("s").into() }
697 }
698 }
699
700 impl ToVariant for str {
to_variant(&self) -> Variant701 fn to_variant(&self) -> Variant {
702 unsafe { from_glib_none(ffi::g_variant_new_take_string(self.to_glib_full())) }
703 }
704 }
705
706 impl<T: StaticVariantType> StaticVariantType for Option<T> {
static_variant_type() -> Cow<'static, VariantTy>707 fn static_variant_type() -> Cow<'static, VariantTy> {
708 let child_type = T::static_variant_type();
709 let signature = format!("m{}", child_type.to_str());
710
711 VariantType::new(&signature)
712 .expect("incorrect signature")
713 .into()
714 }
715 }
716
717 impl<T: StaticVariantType + ToVariant> ToVariant for Option<T> {
to_variant(&self) -> Variant718 fn to_variant(&self) -> Variant {
719 Variant::from_maybe::<T>(self.as_ref().map(|m| m.to_variant()).as_ref())
720 }
721 }
722
723 impl<T: StaticVariantType + FromVariant> FromVariant for Option<T> {
from_variant(variant: &Variant) -> Option<Self>724 fn from_variant(variant: &Variant) -> Option<Self> {
725 unsafe {
726 if variant.is::<Self>() {
727 let c_child = ffi::g_variant_get_maybe(variant.to_glib_none().0);
728 if !c_child.is_null() {
729 let child: Variant = from_glib_full(c_child);
730
731 Some(T::from_variant(&child))
732 } else {
733 Some(None)
734 }
735 } else {
736 None
737 }
738 }
739 }
740 }
741
742 impl<T: StaticVariantType> StaticVariantType for [T] {
static_variant_type() -> Cow<'static, VariantTy>743 fn static_variant_type() -> Cow<'static, VariantTy> {
744 T::static_variant_type().with_array().into()
745 }
746 }
747
748 impl<T: StaticVariantType + ToVariant> ToVariant for [T] {
to_variant(&self) -> Variant749 fn to_variant(&self) -> Variant {
750 let mut vec = Vec::with_capacity(self.len());
751 for child in self {
752 vec.push(child.to_variant());
753 }
754 Variant::from_array::<T>(&vec)
755 }
756 }
757
758 impl<T: FromVariant> FromVariant for Vec<T> {
from_variant(variant: &Variant) -> Option<Self>759 fn from_variant(variant: &Variant) -> Option<Self> {
760 if !variant.is_container() {
761 return None;
762 }
763
764 let mut vec = Vec::with_capacity(variant.n_children());
765
766 for i in 0..variant.n_children() {
767 match variant.child_value(i).get() {
768 Some(child) => vec.push(child),
769 None => return None,
770 }
771 }
772
773 Some(vec)
774 }
775 }
776
777 impl<T: StaticVariantType + ToVariant> ToVariant for Vec<T> {
to_variant(&self) -> Variant778 fn to_variant(&self) -> Variant {
779 let mut vec = Vec::with_capacity(self.len());
780 for child in self {
781 vec.push(child.to_variant());
782 }
783 Variant::from_array::<T>(&vec)
784 }
785 }
786
787 impl<T: StaticVariantType> StaticVariantType for Vec<T> {
static_variant_type() -> Cow<'static, VariantTy>788 fn static_variant_type() -> Cow<'static, VariantTy> {
789 <[T]>::static_variant_type()
790 }
791 }
792
793 #[test]
test_regression_from_variant_panics()794 fn test_regression_from_variant_panics() {
795 let variant = "text".to_variant();
796 let hashmap: Option<HashMap<u64, u64>> = FromVariant::from_variant(&variant);
797 assert!(hashmap.is_none());
798
799 let variant = HashMap::<u64, u64>::new().to_variant();
800 let hashmap: Option<HashMap<u64, u64>> = FromVariant::from_variant(&variant);
801 assert!(hashmap.is_some());
802 }
803
804 impl<K, V, H> FromVariant for HashMap<K, V, H>
805 where
806 K: FromVariant + Eq + Hash,
807 V: FromVariant,
808 H: BuildHasher + Default,
809 {
from_variant(variant: &Variant) -> Option<Self>810 fn from_variant(variant: &Variant) -> Option<Self> {
811 if !variant.is_container() {
812 return None;
813 }
814
815 let mut map = HashMap::default();
816
817 for i in 0..variant.n_children() {
818 let entry = variant.child_value(i);
819 let key = match entry.child_value(0).get() {
820 Some(key) => key,
821 None => return None,
822 };
823 let val = match entry.child_value(1).get() {
824 Some(val) => val,
825 None => return None,
826 };
827
828 map.insert(key, val);
829 }
830
831 Some(map)
832 }
833 }
834
835 impl<K, V> ToVariant for HashMap<K, V>
836 where
837 K: StaticVariantType + ToVariant + Eq + Hash,
838 V: StaticVariantType + ToVariant,
839 {
to_variant(&self) -> Variant840 fn to_variant(&self) -> Variant {
841 let mut vec = Vec::with_capacity(self.len());
842 for (key, value) in self {
843 let entry = DictEntry::new(key, value).to_variant();
844 vec.push(entry);
845 }
846 Variant::from_array::<DictEntry<K, V>>(&vec)
847 }
848 }
849
850 /// A Dictionary entry.
851 ///
852 /// While GVariant format allows a dictionary entry to be an independent type, typically you'll need
853 /// to use this in a dictionary, which is simply an array of dictionary entries. The following code
854 /// creates a dictionary:
855 ///
856 /// ```
857 ///# use glib::prelude::*; // or `use gtk::prelude::*;`
858 /// use glib::{Variant, FromVariant, ToVariant};
859 /// use glib::variant::DictEntry;
860 ///
861 /// let entries = vec![
862 /// DictEntry::new("uuid", 1000u32).to_variant(),
863 /// DictEntry::new("guid", 1001u32).to_variant(),
864 /// ];
865 /// let dict = Variant::from_array::<DictEntry<&str, u32>>(&entries);
866 /// assert_eq!(dict.n_children(), 2);
867 /// assert_eq!(dict.type_().to_str(), "a{su}");
868 /// ```
869 pub struct DictEntry<K, V> {
870 key: K,
871 value: V,
872 }
873
874 impl<K, V> DictEntry<K, V>
875 where
876 K: StaticVariantType + ToVariant + Eq + Hash,
877 V: StaticVariantType + ToVariant,
878 {
new(key: K, value: V) -> Self879 pub fn new(key: K, value: V) -> Self {
880 Self { key, value }
881 }
882
key(&self) -> &K883 pub fn key(&self) -> &K {
884 &self.key
885 }
886
value(&self) -> &V887 pub fn value(&self) -> &V {
888 &self.value
889 }
890 }
891
892 impl<K, V> FromVariant for DictEntry<K, V>
893 where
894 K: FromVariant + Eq + Hash,
895 V: FromVariant,
896 {
from_variant(variant: &Variant) -> Option<Self>897 fn from_variant(variant: &Variant) -> Option<Self> {
898 let key = match variant.child_value(0).get() {
899 Some(key) => key,
900 None => return None,
901 };
902 let value = match variant.child_value(1).get() {
903 Some(value) => value,
904 None => return None,
905 };
906
907 Some(Self { key, value })
908 }
909 }
910
911 impl<K, V> ToVariant for DictEntry<K, V>
912 where
913 K: StaticVariantType + ToVariant + Eq + Hash,
914 V: StaticVariantType + ToVariant,
915 {
to_variant(&self) -> Variant916 fn to_variant(&self) -> Variant {
917 unsafe {
918 from_glib_none(ffi::g_variant_new_dict_entry(
919 self.key.to_variant().to_glib_none().0,
920 self.value.to_variant().to_glib_none().0,
921 ))
922 }
923 }
924 }
925
926 impl ToVariant for Variant {
to_variant(&self) -> Variant927 fn to_variant(&self) -> Variant {
928 Variant::from_variant(self)
929 }
930 }
931
932 impl FromVariant for Variant {
from_variant(variant: &Variant) -> Option<Self>933 fn from_variant(variant: &Variant) -> Option<Self> {
934 variant.as_variant()
935 }
936 }
937
938 impl<K: StaticVariantType, V: StaticVariantType> StaticVariantType for DictEntry<K, V> {
static_variant_type() -> Cow<'static, VariantTy>939 fn static_variant_type() -> Cow<'static, VariantTy> {
940 let key_type = K::static_variant_type();
941 let value_type = V::static_variant_type();
942 let signature = format!("{{{}{}}}", key_type.to_str(), value_type.to_str());
943
944 VariantType::new(&signature)
945 .expect("incorrect signature")
946 .into()
947 }
948 }
949
950 impl<K, V, H> StaticVariantType for HashMap<K, V, H>
951 where
952 K: StaticVariantType,
953 V: StaticVariantType,
954 H: BuildHasher + Default,
955 {
static_variant_type() -> Cow<'static, VariantTy>956 fn static_variant_type() -> Cow<'static, VariantTy> {
957 let key_type = K::static_variant_type();
958 let value_type = V::static_variant_type();
959 let signature = format!("a{{{}{}}}", key_type.to_str(), value_type.to_str());
960
961 VariantType::new(&signature)
962 .expect("incorrect signature")
963 .into()
964 }
965 }
966
967 macro_rules! tuple_impls {
968 ($($len:expr => ($($n:tt $name:ident)+))+) => {
969 $(
970 impl<$($name),+> StaticVariantType for ($($name,)+)
971 where
972 $($name: StaticVariantType,)+
973 {
974 fn static_variant_type() -> Cow<'static, VariantTy> {
975 let mut signature = String::with_capacity(255);
976 signature.push('(');
977 $(
978 signature.push_str($name::static_variant_type().to_str());
979 )+
980 signature.push(')');
981
982 VariantType::new(&signature).expect("incorrect signature").into()
983 }
984 }
985
986 impl<$($name),+> FromVariant for ($($name,)+)
987 where
988 $($name: FromVariant,)+
989 {
990 fn from_variant(variant: &Variant) -> Option<Self> {
991 Some((
992 $(
993 match $name::from_variant(&variant.child_value($n)) {
994 Some(field) => field,
995 None => return None,
996 },
997 )+
998 ))
999 }
1000 }
1001
1002 impl<$($name),+> ToVariant for ($($name,)+)
1003 where
1004 $($name: ToVariant,)+
1005 {
1006 fn to_variant(&self) -> Variant {
1007 let mut fields = Vec::with_capacity($len);
1008 $(
1009 let field = self.$n.to_variant();
1010 fields.push(field);
1011 )+
1012 Variant::from_tuple(&fields)
1013 }
1014 }
1015 )+
1016 }
1017 }
1018
1019 tuple_impls! {
1020 1 => (0 T0)
1021 2 => (0 T0 1 T1)
1022 3 => (0 T0 1 T1 2 T2)
1023 4 => (0 T0 1 T1 2 T2 3 T3)
1024 5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
1025 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
1026 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
1027 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
1028 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
1029 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
1030 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
1031 12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
1032 13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
1033 14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
1034 15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
1035 16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
1036 }
1037
1038 #[cfg(test)]
1039 mod tests {
1040 use super::*;
1041 use std::collections::{HashMap, HashSet};
1042
1043 macro_rules! unsigned {
1044 ($name:ident, $ty:ident) => {
1045 #[test]
1046 fn $name() {
1047 let mut n = $ty::max_value();
1048 while n > 0 {
1049 let v = n.to_variant();
1050 assert_eq!(v.get(), Some(n));
1051 n /= 2;
1052 }
1053 }
1054 };
1055 }
1056
1057 macro_rules! signed {
1058 ($name:ident, $ty:ident) => {
1059 #[test]
1060 fn $name() {
1061 let mut n = $ty::max_value();
1062 while n > 0 {
1063 let v = n.to_variant();
1064 assert_eq!(v.get(), Some(n));
1065 let v = (-n).to_variant();
1066 assert_eq!(v.get(), Some(-n));
1067 n /= 2;
1068 }
1069 }
1070 };
1071 }
1072
1073 unsigned!(test_u8, u8);
1074 unsigned!(test_u16, u16);
1075 unsigned!(test_u32, u32);
1076 unsigned!(test_u64, u64);
1077 signed!(test_i16, i16);
1078 signed!(test_i32, i32);
1079 signed!(test_i64, i64);
1080
1081 #[test]
test_str()1082 fn test_str() {
1083 let s = "this is a test";
1084 let v = s.to_variant();
1085 assert_eq!(v.str(), Some(s));
1086 assert_eq!(42u32.to_variant().str(), None);
1087 }
1088
1089 #[test]
test_bytes()1090 fn test_bytes() {
1091 let b = b"this is a test";
1092 let v = b.to_variant();
1093 assert_eq!(v.bytes().unwrap(), b);
1094 assert!(42u32.to_variant().bytes().is_err());
1095 }
1096
1097 #[test]
test_string()1098 fn test_string() {
1099 let s = String::from("this is a test");
1100 let v = s.to_variant();
1101 assert_eq!(v.get(), Some(s));
1102 assert_eq!(v.normal_form(), v);
1103 }
1104
1105 #[test]
test_eq()1106 fn test_eq() {
1107 let v1 = "this is a test".to_variant();
1108 let v2 = "this is a test".to_variant();
1109 let v3 = "test".to_variant();
1110 assert_eq!(v1, v2);
1111 assert_ne!(v1, v3);
1112 }
1113
1114 #[test]
test_hash()1115 fn test_hash() {
1116 let v1 = "this is a test".to_variant();
1117 let v2 = "this is a test".to_variant();
1118 let v3 = "test".to_variant();
1119 let mut set = HashSet::new();
1120 set.insert(v1);
1121 assert!(set.contains(&v2));
1122 assert!(!set.contains(&v3));
1123
1124 assert_eq!(
1125 <HashMap<&str, (&str, u8, u32)>>::static_variant_type().to_str(),
1126 "a{s(syu)}"
1127 );
1128 }
1129
1130 #[test]
test_array()1131 fn test_array() {
1132 assert_eq!(<Vec<&str>>::static_variant_type().to_str(), "as");
1133 assert_eq!(
1134 <Vec<(&str, u8, u32)>>::static_variant_type().to_str(),
1135 "a(syu)"
1136 );
1137 let a = ["foo", "bar", "baz"].to_variant();
1138 assert_eq!(a.normal_form(), a);
1139 assert_eq!(a.array_iter_str().unwrap().len(), 3);
1140 let o = 0u32.to_variant();
1141 assert!(o.array_iter_str().is_err());
1142 }
1143
1144 #[test]
test_get() -> Result<(), Box<dyn std::error::Error>>1145 fn test_get() -> Result<(), Box<dyn std::error::Error>> {
1146 let u = 42u32.to_variant();
1147 assert!(u.get::<i32>().is_none());
1148 assert_eq!(u.get::<u32>().unwrap(), 42);
1149 assert!(u.try_get::<i32>().is_err());
1150 // Test ? conversion
1151 assert_eq!(u.try_get::<u32>()?, 42);
1152 Ok(())
1153 }
1154
1155 #[test]
test_byteswap()1156 fn test_byteswap() {
1157 let u = 42u32.to_variant();
1158 assert_eq!(u.byteswap().get::<u32>().unwrap(), 704643072u32);
1159 assert_eq!(u.byteswap().byteswap().get::<u32>().unwrap(), 42u32);
1160 }
1161
1162 #[test]
test_try_child()1163 fn test_try_child() {
1164 let a = ["foo"].to_variant();
1165 assert!(a.try_child_value(0).is_some());
1166 assert_eq!(a.try_child_get::<String>(0).unwrap().unwrap(), "foo");
1167 assert_eq!(a.child_get::<String>(0), "foo");
1168 assert!(a.try_child_get::<u32>(0).is_err());
1169 assert!(a.try_child_value(1).is_none());
1170 assert!(a.try_child_get::<String>(1).unwrap().is_none());
1171 let u = 42u32.to_variant();
1172 assert!(u.try_child_value(0).is_none());
1173 assert!(u.try_child_get::<String>(0).unwrap().is_none());
1174 }
1175 }
1176