From 8a475660f17ff28d627ea4a552783bc79d09b28f Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Fri, 5 Jun 2020 15:55:53 +0200 Subject: [PATCH 01/11] Implement StaticVariantType for common Rust container types This will allow us to create Variant instances from encoded data for container types. The API to query and manipulate a container carying Variant will follow in another patch. --- src/variant.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/src/variant.rs b/src/variant.rs index e60ef03d..a6fe549b 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -45,7 +45,7 @@ use gstring::GString; use std::borrow::Cow; use std::cmp::{Eq, Ordering, PartialEq, PartialOrd}; use std::fmt; -use std::hash::{Hash, Hasher}; +use std::hash::{BuildHasher, Hash, Hasher}; use std::slice; use std::str; use translate::*; @@ -54,6 +54,7 @@ use StaticType; use Type; use Value; use VariantTy; +use VariantType; glib_wrapper! { /// A generic immutable value capable of carrying various types. @@ -378,10 +379,82 @@ impl From for Variant { } } +impl StaticVariantType for [T] { + fn static_variant_type() -> Cow<'static, VariantTy> { + let child_type = T::static_variant_type(); + let signature = format!("a{}", child_type.to_str()); + + VariantType::new(&signature) + .expect("incorrect signature") + .into() + } +} + +impl StaticVariantType for Vec { + fn static_variant_type() -> Cow<'static, VariantTy> { + <[T]>::static_variant_type() + } +} + +impl StaticVariantType + for HashMap +{ + fn static_variant_type() -> Cow<'static, VariantTy> { + let key_type = K::static_variant_type(); + let value_type = V::static_variant_type(); + let signature = format!("a{{{}{}}}", key_type.to_str(), value_type.to_str()); + + VariantType::new(&signature) + .expect("incorrect signature") + .into() + } +} + +macro_rules! tuple_impls { + ($($len:expr => ($($n:tt $name:ident)+))+) => { + $( + impl<$($name),+> StaticVariantType for ($($name,)+) + where + $($name: StaticVariantType,)+ + { + fn static_variant_type() -> Cow<'static, VariantTy> { + let mut signature = String::with_capacity(255); + signature.push('('); + $( + signature.push_str($name::static_variant_type().to_str()); + )+ + signature.push(')'); + + VariantType::new(&signature).expect("incorrect signature").into() + } + } + )+ + } +} + +tuple_impls! { + 1 => (0 T0) + 2 => (0 T0 1 T1) + 3 => (0 T0 1 T1 2 T2) + 4 => (0 T0 1 T1 2 T2 3 T3) + 5 => (0 T0 1 T1 2 T2 3 T3 4 T4) + 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) + 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) + 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) + 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) + 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) + 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) + 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) + 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) + 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) + 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) + 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) +} + #[cfg(test)] mod tests { use super::*; - use std::collections::HashSet; + use std::collections::{HashMap, HashSet}; macro_rules! unsigned { ($name:ident, $ty:ident) => { @@ -453,5 +526,20 @@ mod tests { set.insert(v1); assert!(set.contains(&v2)); assert!(!set.contains(&v3)); + + assert_eq!( + >::static_variant_type().to_str(), + "a{s(syu)}" + ); + } + + #[test] + fn test_array() { + // Test just the signature for now. + assert_eq!(>::static_variant_type().to_str(), "as"); + assert_eq!( + >::static_variant_type().to_str(), + "a(syu)" + ); } } From 2d616d68799553a8c081aebf389a69457a680bd5 Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Wed, 3 Jun 2020 18:14:06 +0200 Subject: [PATCH 02/11] Bindings for Variant boxing/unboxing API --- src/variant.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/variant.rs b/src/variant.rs index a6fe549b..47d6bef4 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -36,6 +36,11 @@ //! // `get_str` tries to borrow a string slice. //! assert_eq!(hello.get_str(), Some("Hello!")); //! assert_eq!(num.get_str(), None); +//! +//! // Variant carrying a Variant +//! let variant = Variant::new_variant(&hello); +//! let variant = variant.get_variant().unwrap(); +//! assert_eq!(variant.get_str(), Some("Hello!")); //! ``` use bytes::Bytes; @@ -123,6 +128,20 @@ impl Variant { T::from_variant(self) } + /// Boxes value. + #[inline] + pub fn new_variant(value: &Variant) -> Self { + unsafe { from_glib_full(glib_sys::g_variant_new_variant(value.to_glib_full())) } + } + + /// Unboxes self. + /// + /// Returns `Some` if self contains a `Variant`. + #[inline] + pub fn get_variant(&self) -> Option { + unsafe { from_glib_none(glib_sys::g_variant_get_variant(self.to_glib_none().0)) } + } + /// Tries to extract a `&str`. /// /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type From 86c7395a61e520e077b63b86e44bfea05ea65f64 Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Wed, 3 Jun 2020 18:22:26 +0200 Subject: [PATCH 03/11] impl StaticVariantType for Variant So that `new_from_bytes` can be used with Variant type. --- src/variant.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/variant.rs b/src/variant.rs index 47d6bef4..e75218c3 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -287,6 +287,12 @@ pub trait StaticVariantType { fn static_variant_type() -> Cow<'static, VariantTy>; } +impl StaticVariantType for Variant { + fn static_variant_type() -> Cow<'static, VariantTy> { + unsafe { VariantTy::from_str_unchecked("v").into() } + } +} + impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T { fn to_variant(&self) -> Variant { ::to_variant(self) From 300ef3565d5fde6cdb55e9ef941f7c6ec2de58db Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Thu, 4 Jun 2020 15:03:10 +0200 Subject: [PATCH 04/11] Basic API for container-type Variant --- src/variant.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/variant.rs b/src/variant.rs index e75218c3..b0f07621 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -142,6 +142,25 @@ impl Variant { unsafe { from_glib_none(glib_sys::g_variant_get_variant(self.to_glib_none().0)) } } + /// Reads a child item out of a container `Variant` instance. + /// + /// # Panics + /// + /// * if `self` is not a container type. + /// * if given `index` is larger than number of children. + pub fn get_child_value(&self, index: usize) -> Variant { + assert!(index < self.n_children()); + let ty = self.type_().to_str(); + assert!(ty.starts_with("a") || ty.starts_with("{") || ty.starts_with("(")); + + unsafe { + from_glib_none(glib_sys::g_variant_get_child_value( + self.to_glib_none().0, + index, + )) + } + } + /// Tries to extract a `&str`. /// /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type @@ -198,6 +217,20 @@ impl Variant { pub fn get_data_as_bytes(&self) -> Bytes { unsafe { from_glib_full(glib_sys::g_variant_get_data_as_bytes(self.to_glib_none().0)) } } + + /// Determines the number of children in a container GVariant instance. + pub fn n_children(&self) -> usize { + let type_ = self.type_().to_str(); + assert!( + type_.starts_with("a") + || type_.starts_with("m") + || type_.starts_with("(") + || type_.starts_with("{") + || type_.starts_with("v") + ); + + unsafe { glib_sys::g_variant_n_children(self.to_glib_none().0) } + } } unsafe impl Send for Variant {} From df1b340235d5e66df6fadab7278c21b823a77497 Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Thu, 4 Jun 2020 15:03:36 +0200 Subject: [PATCH 05/11] Allow creating Array type Variant --- src/variant.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/variant.rs b/src/variant.rs index b0f07621..8d302343 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -41,6 +41,13 @@ //! let variant = Variant::new_variant(&hello); //! let variant = variant.get_variant().unwrap(); //! assert_eq!(variant.get_str(), Some("Hello!")); +//! +//! // Variant carrying an array +//! let array = [Variant::from("Hello"), Variant::from("there!")]; +//! let variant = Variant::new_array::<&str>(&array); +//! assert_eq!(variant.n_children(), 2); +//! assert_eq!(variant.get_child_value(0).get_str(), Some("Hello")); +//! assert_eq!(variant.get_child_value(1).get_str(), Some("there!")); //! ``` use bytes::Bytes; @@ -182,6 +189,24 @@ impl Variant { } } + /// Creates a new GVariant array from children. + /// + /// All children must be of type `T`. + pub fn new_array(children: &[Variant]) -> Self { + let type_ = T::static_variant_type(); + + for child in children { + assert_eq!(type_, child.type_()); + } + unsafe { + from_glib_none(glib_sys::g_variant_new_array( + type_.as_ptr() as *const _, + children.to_glib_none().0, + children.len(), + )) + } + } + /// Constructs a new serialised-mode GVariant instance. pub fn from_bytes(bytes: &Bytes) -> Self { unsafe { From 733f152c106af93a2d26dd700a6abd37b93c9e7b Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Tue, 1 Sep 2020 19:29:13 +0200 Subject: [PATCH 06/11] Export FromVariant in public API --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 757b02c9..63425811 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,7 +117,7 @@ pub use enums::{EnumClass, EnumValue, FlagsBuilder, FlagsClass, FlagsValue, User pub use time_val::{get_current_time, TimeVal}; pub use types::{StaticType, Type}; pub use value::{SendValue, ToSendValue, ToValue, TypedValue, Value}; -pub use variant::{StaticVariantType, ToVariant, Variant}; +pub use variant::{FromVariant, StaticVariantType, ToVariant, Variant}; pub use variant_dict::VariantDict; pub use variant_type::{VariantTy, VariantType}; From fe257e4c7bcf819301e909b0370a6e01be31534d Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Tue, 1 Sep 2020 19:29:48 +0200 Subject: [PATCH 07/11] Add conversion between Vec and Variant --- src/variant.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/variant.rs b/src/variant.rs index 8d302343..67cf1861 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -18,7 +18,7 @@ //! //! ``` //! use glib::prelude::*; // or `use gtk::prelude::*;` -//! use glib::Variant; +//! use glib::{Variant, FromVariant, ToVariant}; //! //! // Using the `ToVariant` trait. //! let num = 10.to_variant(); @@ -48,6 +48,12 @@ //! assert_eq!(variant.n_children(), 2); //! assert_eq!(variant.get_child_value(0).get_str(), Some("Hello")); //! assert_eq!(variant.get_child_value(1).get_str(), Some("there!")); +//! +//! // You can also convert from and to a Vec +//! let array = vec!["Hello", "there!"].to_variant(); +//! assert_eq!(variant.n_children(), 2); +//! let vec = >::from_variant(&array).unwrap(); +//! assert_eq!(vec[0], "Hello"); //! ``` use bytes::Bytes; @@ -473,6 +479,31 @@ impl StaticVariantType for [T] { } } +impl FromVariant for Vec { + fn from_variant(variant: &Variant) -> Option { + let mut vec = Vec::with_capacity(variant.n_children()); + + for i in 0..variant.n_children() { + match variant.get_child_value(i).get() { + Some(child) => vec.push(child), + None => return None, + } + } + + Some(vec) + } +} + +impl ToVariant for Vec { + fn to_variant(&self) -> Variant { + let mut vec = Vec::with_capacity(self.len()); + for child in self { + vec.push(child.to_variant()); + } + Variant::new_array::(&vec) + } +} + impl StaticVariantType for Vec { fn static_variant_type() -> Cow<'static, VariantTy> { <[T]>::static_variant_type() From 3ee231337b3ee4ffbb6d12c4aac87057b910af2b Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Wed, 2 Sep 2020 17:28:48 +0200 Subject: [PATCH 08/11] API for creating dictionary entries --- src/variant.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/src/variant.rs b/src/variant.rs index 67cf1861..3a866d37 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -62,6 +62,7 @@ use gobject_sys; use gstring::GString; use std::borrow::Cow; use std::cmp::{Eq, Ordering, PartialEq, PartialOrd}; +use std::collections::HashMap; use std::fmt; use std::hash::{BuildHasher, Hash, Hasher}; use std::slice; @@ -510,8 +511,99 @@ impl StaticVariantType for Vec { } } -impl StaticVariantType - for HashMap +/// A Dictionary entry. +/// +/// While GVariant format allows a dictionary entry to be an independent type, typically you'll need +/// to use this in a dictionary, which is simply an array of dictionary entries. The following code +/// creates a dictionary: +/// +/// ``` +///# use glib::prelude::*; // or `use gtk::prelude::*;` +/// use glib::{Variant, FromVariant, ToVariant}; +/// use glib::variant::DictEntry; +/// +/// let entries = vec![ +/// DictEntry::new("uuid", 1000u32).to_variant(), +/// DictEntry::new("guid", 1001u32).to_variant(), +/// ]; +/// let dict = Variant::new_array::>(&entries); +/// assert_eq!(dict.n_children(), 2); +/// assert_eq!(dict.type_().to_str(), "a{su}"); +/// ``` +pub struct DictEntry { + key: K, + value: V, +} + +impl DictEntry +where + K: StaticVariantType + ToVariant + Eq + Hash, + V: StaticVariantType + ToVariant, +{ + pub fn new(key: K, value: V) -> Self { + DictEntry { key, value } + } + + pub fn key(&self) -> &K { + &self.key + } + + pub fn value(&self) -> &V { + &self.value + } +} + +impl FromVariant for DictEntry +where + K: FromVariant + Eq + Hash, + V: FromVariant, +{ + fn from_variant(variant: &Variant) -> Option { + let key = match variant.get_child_value(0).get() { + Some(key) => key, + None => return None, + }; + let value = match variant.get_child_value(1).get() { + Some(value) => value, + None => return None, + }; + + Some(DictEntry { key, value }) + } +} + +impl ToVariant for DictEntry +where + K: StaticVariantType + ToVariant + Eq + Hash, + V: StaticVariantType + ToVariant, +{ + fn to_variant(&self) -> Variant { + unsafe { + from_glib_none(glib_sys::g_variant_new_dict_entry( + self.key.to_variant().to_glib_none().0, + self.value.to_variant().to_glib_none().0, + )) + } + } +} + +impl StaticVariantType for DictEntry { + fn static_variant_type() -> Cow<'static, VariantTy> { + let key_type = K::static_variant_type(); + let value_type = V::static_variant_type(); + let signature = format!("{{{}{}}}", key_type.to_str(), value_type.to_str()); + + VariantType::new(&signature) + .expect("incorrect signature") + .into() + } +} + +impl StaticVariantType for HashMap +where + K: StaticVariantType, + V: StaticVariantType, + H: BuildHasher + Default, { fn static_variant_type() -> Cow<'static, VariantTy> { let key_type = K::static_variant_type(); From 12300198d0167848051976cbc9632fae818bbae8 Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Wed, 2 Sep 2020 17:29:14 +0200 Subject: [PATCH 09/11] Add conversion between Variant and HashMap --- src/variant.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/variant.rs b/src/variant.rs index 3a866d37..f20d9397 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -19,6 +19,7 @@ //! ``` //! use glib::prelude::*; // or `use gtk::prelude::*;` //! use glib::{Variant, FromVariant, ToVariant}; +//! use std::collections::HashMap; //! //! // Using the `ToVariant` trait. //! let num = 10.to_variant(); @@ -54,6 +55,16 @@ //! assert_eq!(variant.n_children(), 2); //! let vec = >::from_variant(&array).unwrap(); //! assert_eq!(vec[0], "Hello"); +//! +//! // Conversion to and from HashMap is also possible +//! let mut map: HashMap = HashMap::new(); +//! map.insert(1, "hi"); +//! map.insert(2, "there"); +//! let variant = map.to_variant(); +//! assert_eq!(variant.n_children(), 2); +//! let map: HashMap = HashMap::from_variant(&variant).unwrap(); +//! assert_eq!(map[&1], "hi"); +//! assert_eq!(map[&2], "there"); //! ``` use bytes::Bytes; @@ -511,6 +522,48 @@ impl StaticVariantType for Vec { } } +impl FromVariant for HashMap +where + K: FromVariant + Eq + Hash, + V: FromVariant, + H: BuildHasher + Default, +{ + fn from_variant(variant: &Variant) -> Option { + let mut map = HashMap::default(); + + for i in 0..variant.n_children() { + let entry = variant.get_child_value(i); + let key = match entry.get_child_value(0).get() { + Some(key) => key, + None => return None, + }; + let val = match entry.get_child_value(1).get() { + Some(val) => val, + None => return None, + }; + + map.insert(key, val); + } + + Some(map) + } +} + +impl ToVariant for HashMap +where + K: StaticVariantType + ToVariant + Eq + Hash, + V: StaticVariantType + ToVariant, +{ + fn to_variant(&self) -> Variant { + let mut vec = Vec::with_capacity(self.len()); + for (key, value) in self { + let entry = DictEntry::new(key, value).to_variant(); + vec.push(entry); + } + Variant::new_array::>(&vec) + } +} + /// A Dictionary entry. /// /// While GVariant format allows a dictionary entry to be an independent type, typically you'll need From 1deff1a88562f8c458d1aef2dde9e03e2c4e59f4 Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Wed, 2 Sep 2020 20:14:03 +0200 Subject: [PATCH 10/11] API to create tuple Variant & convert it to/from tuples --- src/variant.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/variant.rs b/src/variant.rs index f20d9397..c4f1978e 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -65,6 +65,15 @@ //! let map: HashMap = HashMap::from_variant(&variant).unwrap(); //! assert_eq!(map[&1], "hi"); //! assert_eq!(map[&2], "there"); +//! +//! // And conversion to and from tuples. +//! let variant = ("hello", 42u16, vec![ "there", "you" ],).to_variant(); +//! assert_eq!(variant.n_children(), 3); +//! assert_eq!(variant.type_().to_str(), "(sqas)"); +//! let tuple = <(String, u16, Vec)>::from_variant(&variant).unwrap(); +//! assert_eq!(tuple.0, "hello"); +//! assert_eq!(tuple.1, 42); +//! assert_eq!(tuple.2, &[ "there", "you"]); //! ``` use bytes::Bytes; @@ -225,6 +234,16 @@ impl Variant { } } + /// Creates a new GVariant tuple from children. + pub fn new_tuple(children: &[Variant]) -> Self { + unsafe { + from_glib_none(glib_sys::g_variant_new_tuple( + children.to_glib_none().0, + children.len(), + )) + } + } + /// Constructs a new serialised-mode GVariant instance. pub fn from_bytes(bytes: &Bytes) -> Self { unsafe { @@ -687,6 +706,36 @@ macro_rules! tuple_impls { VariantType::new(&signature).expect("incorrect signature").into() } } + + impl<$($name),+> FromVariant for ($($name,)+) + where + $($name: FromVariant,)+ + { + fn from_variant(variant: &Variant) -> Option { + Some(( + $( + match $name::from_variant(&variant.get_child_value($n)) { + Some(field) => field, + None => return None, + }, + )+ + )) + } + } + + impl<$($name),+> ToVariant for ($($name,)+) + where + $($name: ToVariant,)+ + { + fn to_variant(&self) -> Variant { + let mut fields = Vec::with_capacity($len); + $( + let field = self.$n.to_variant(); + fields.push(field); + )+ + Variant::new_tuple(&fields) + } + } )+ } } From 4a9a20318331ba03561127f82f6ff8078c1d2a85 Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Thu, 3 Sep 2020 15:19:15 +0200 Subject: [PATCH 11/11] Variant support for Option (Maybe type of GVariant) --- src/variant.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/variant.rs b/src/variant.rs index c4f1978e..d749ffa6 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -74,6 +74,17 @@ //! assert_eq!(tuple.0, "hello"); //! assert_eq!(tuple.1, 42); //! assert_eq!(tuple.2, &[ "there", "you"]); +//! +//! // `Option` is supported as well, through maybe types +//! let variant = Some("hello").to_variant(); +//! assert_eq!(variant.n_children(), 1); +//! let mut s = >::from_variant(&variant).unwrap(); +//! assert_eq!(s.unwrap(), "hello"); +//! s = None; +//! let variant = s.to_variant(); +//! assert_eq!(variant.n_children(), 0); +//! let s = >::from_variant(&variant).unwrap(); +//! assert!(s.is_none()); //! ``` use bytes::Bytes; @@ -244,6 +255,25 @@ impl Variant { } } + /// Creates a new maybe Variant. + pub fn new_maybe(child: Option<&Variant>) -> Self { + let type_ = T::static_variant_type(); + let ptr = match child { + Some(child) => { + assert_eq!(type_, child.type_()); + + child.to_glib_none().0 + } + None => std::ptr::null(), + }; + unsafe { + from_glib_none(glib_sys::g_variant_new_maybe( + type_.as_ptr() as *const _, + ptr as *mut glib_sys::GVariant, + )) + } + } + /// Constructs a new serialised-mode GVariant instance. pub fn from_bytes(bytes: &Bytes) -> Self { unsafe { @@ -499,6 +529,42 @@ impl From for Variant { } } +impl StaticVariantType for Option { + fn static_variant_type() -> Cow<'static, VariantTy> { + let child_type = T::static_variant_type(); + let signature = format!("m{}", child_type.to_str()); + + VariantType::new(&signature) + .expect("incorrect signature") + .into() + } +} + +impl ToVariant for Option { + fn to_variant(&self) -> Variant { + Variant::new_maybe::(self.as_ref().map(|m| m.to_variant()).as_ref()) + } +} + +impl FromVariant for Option { + fn from_variant(variant: &Variant) -> Option { + unsafe { + if variant.is::() { + let c_child = glib_sys::g_variant_get_maybe(variant.to_glib_none().0); + if !c_child.is_null() { + let child: Variant = from_glib_full(c_child); + + Some(T::from_variant(&child)) + } else { + Some(None) + } + } else { + None + } + } + } +} + impl StaticVariantType for [T] { fn static_variant_type() -> Cow<'static, VariantTy> { let child_type = T::static_variant_type();