// Take a look at the license at the top of the repository in the LICENSE file. // rustdoc-stripper-ignore-next //! `Variant` binding and helper traits. //! //! [`Variant`](struct.Variant.html) is an immutable dynamically-typed generic //! container. Its type and value are defined at construction and never change. //! //! `Variant` types are described by [`VariantType`](../struct.VariantType.html) //! "type strings". //! //! `GVariant` supports arbitrarily complex types built from primitives like integers, floating point //! numbers, strings, arrays, tuples and dictionaries. See [`ToVariant#foreign-impls`] for //! a full list of supported types. You may also implement [`ToVariant`] and [`FromVariant`] //! manually, or derive them using the [`Variant`](derive@crate::Variant) derive macro. //! //! # Examples //! //! ``` //! 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(); //! //! // `is` tests the type of the value. //! assert!(num.is::()); //! //! // `get` tries to extract the value. //! assert_eq!(num.get::(), Some(10)); //! assert_eq!(num.get::(), None); //! //! // `get_str` tries to borrow a string slice. //! let hello = "Hello!".to_variant(); //! assert_eq!(hello.str(), Some("Hello!")); //! assert_eq!(num.str(), None); //! //! // `fixed_array` tries to borrow a fixed size array (u8, bool, i16, etc.), //! // rather than creating a deep copy which would be expensive for //! // nontrivially sized arrays of fixed size elements. //! // The test data here is the zstd compression header, which //! // stands in for arbitrary binary data (e.g. not UTF-8). //! let bufdata = b"\xFD\x2F\xB5\x28"; //! let bufv = glib::Variant::array_from_fixed_array(&bufdata[..]); //! assert_eq!(bufv.fixed_array::().unwrap(), bufdata); //! assert!(num.fixed_array::().is_err()); //! //! // Variant carrying a Variant //! let variant = Variant::from_variant(&hello); //! let variant = variant.as_variant().unwrap(); //! assert_eq!(variant.str(), Some("Hello!")); //! //! // Variant carrying an array //! let array = ["Hello", "there!"]; //! let variant = array.into_iter().collect::(); //! assert_eq!(variant.n_children(), 2); //! assert_eq!(variant.child_value(0).str(), Some("Hello")); //! assert_eq!(variant.child_value(1).str(), Some("there!")); //! //! // You can also convert from and to a Vec //! let variant = vec!["Hello", "there!"].to_variant(); //! assert_eq!(variant.n_children(), 2); //! let vec = >::from_variant(&variant).unwrap(); //! assert_eq!(vec[0], "Hello"); //! //! // Conversion to and from HashMap and BTreeMap 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"); //! //! // 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_().as_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"]); //! //! // `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()); //! //! // Paths may be converted, too. Please note the portability warning above! //! use std::path::{Path, PathBuf}; //! let path = Path::new("foo/bar"); //! let path_variant = path.to_variant(); //! assert_eq!(PathBuf::from_variant(&path_variant).as_deref(), Some(path)); //! ``` use std::{ borrow::Cow, cmp::{Eq, Ordering, PartialEq, PartialOrd}, collections::{BTreeMap, HashMap}, fmt, hash::{BuildHasher, Hash, Hasher}, mem, ptr, slice, str, }; use crate::{ prelude::*, translate::*, Bytes, Type, VariantIter, VariantStrIter, VariantTy, VariantType, }; wrapper! { // rustdoc-stripper-ignore-next /// A generic immutable value capable of carrying various types. /// /// See the [module documentation](index.html) for more details. #[doc(alias = "GVariant")] pub struct Variant(Shared); match fn { ref => |ptr| ffi::g_variant_ref_sink(ptr), unref => |ptr| ffi::g_variant_unref(ptr), } } impl StaticType for Variant { #[inline] fn static_type() -> Type { Type::VARIANT } } #[doc(hidden)] impl crate::value::ValueType for Variant { type Type = Variant; } #[doc(hidden)] impl crate::value::ValueTypeOptional for Variant {} #[doc(hidden)] unsafe impl<'a> crate::value::FromValue<'a> for Variant { type Checker = crate::value::GenericValueTypeOrNoneChecker; unsafe fn from_value(value: &'a crate::Value) -> Self { let ptr = gobject_ffi::g_value_dup_variant(value.to_glib_none().0); debug_assert!(!ptr.is_null()); from_glib_full(ptr) } } #[doc(hidden)] impl crate::value::ToValue for Variant { fn to_value(&self) -> crate::Value { unsafe { let mut value = crate::Value::from_type_unchecked(Variant::static_type()); gobject_ffi::g_value_take_variant(value.to_glib_none_mut().0, self.to_glib_full()); value } } fn value_type(&self) -> crate::Type { Variant::static_type() } } #[doc(hidden)] impl From for crate::Value { #[inline] fn from(v: Variant) -> Self { unsafe { let mut value = crate::Value::from_type_unchecked(Variant::static_type()); gobject_ffi::g_value_take_variant(value.to_glib_none_mut().0, v.into_glib_ptr()); value } } } #[doc(hidden)] impl crate::value::ToValueOptional for Variant { fn to_value_optional(s: Option<&Self>) -> crate::Value { let mut value = crate::Value::for_value_type::(); unsafe { gobject_ffi::g_value_take_variant(value.to_glib_none_mut().0, s.to_glib_full()); } value } } // rustdoc-stripper-ignore-next /// An error returned from the [`try_get`](struct.Variant.html#method.try_get) function /// on a [`Variant`](struct.Variant.html) when the expected type does not match the actual type. #[derive(Clone, PartialEq, Eq, Debug)] pub struct VariantTypeMismatchError { pub actual: VariantType, pub expected: VariantType, } impl VariantTypeMismatchError { pub fn new(actual: VariantType, expected: VariantType) -> Self { Self { actual, expected } } } impl fmt::Display for VariantTypeMismatchError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Type mismatch: Expected '{}' got '{}'", self.expected, self.actual ) } } impl std::error::Error for VariantTypeMismatchError {} impl Variant { // rustdoc-stripper-ignore-next /// Returns the type of the value. #[doc(alias = "g_variant_get_type")] pub fn type_(&self) -> &VariantTy { unsafe { VariantTy::from_ptr(ffi::g_variant_get_type(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Returns `true` if the type of the value corresponds to `T`. #[inline] #[doc(alias = "g_variant_is_of_type")] pub fn is(&self) -> bool { self.is_type(&T::static_variant_type()) } // rustdoc-stripper-ignore-next /// Returns `true` if the type of the value corresponds to `type_`. /// /// This is equivalent to [`self.type_().is_subtype_of(type_)`](VariantTy::is_subtype_of). #[inline] #[doc(alias = "g_variant_is_of_type")] pub fn is_type(&self, type_: &VariantTy) -> bool { unsafe { from_glib(ffi::g_variant_is_of_type( self.to_glib_none().0, type_.to_glib_none().0, )) } } // rustdoc-stripper-ignore-next /// Returns the classification of the variant. #[doc(alias = "g_variant_classify")] pub fn classify(&self) -> crate::VariantClass { unsafe { from_glib(ffi::g_variant_classify(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Tries to extract a value of type `T`. /// /// Returns `Some` if `T` matches the variant's type. #[inline] pub fn get(&self) -> Option { T::from_variant(self) } // rustdoc-stripper-ignore-next /// Tries to extract a value of type `T`. pub fn try_get(&self) -> Result { self.get().ok_or_else(|| { VariantTypeMismatchError::new( self.type_().to_owned(), T::static_variant_type().into_owned(), ) }) } // rustdoc-stripper-ignore-next /// Boxes value. #[inline] pub fn from_variant(value: &Variant) -> Self { unsafe { from_glib_none(ffi::g_variant_new_variant(value.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Unboxes self. /// /// Returns `Some` if self contains a `Variant`. #[inline] #[doc(alias = "get_variant")] pub fn as_variant(&self) -> Option { unsafe { from_glib_full(ffi::g_variant_get_variant(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// 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. #[doc(alias = "get_child_value")] #[doc(alias = "g_variant_get_child_value")] #[must_use] pub fn child_value(&self, index: usize) -> Variant { assert!(self.is_container()); assert!(index < self.n_children()); unsafe { from_glib_full(ffi::g_variant_get_child_value(self.to_glib_none().0, index)) } } // rustdoc-stripper-ignore-next /// Try to read a child item out of a container `Variant` instance. /// /// It returns `None` if `self` is not a container type or if the given /// `index` is larger than number of children. pub fn try_child_value(&self, index: usize) -> Option { if !(self.is_container() && index < self.n_children()) { return None; } let v = unsafe { from_glib_full(ffi::g_variant_get_child_value(self.to_glib_none().0, index)) }; Some(v) } // rustdoc-stripper-ignore-next /// Try to read a child item out of a container `Variant` instance. /// /// It returns `Ok(None)` if `self` is not a container type or if the given /// `index` is larger than number of children. An error is thrown if the /// type does not match. pub fn try_child_get( &self, index: usize, ) -> Result, VariantTypeMismatchError> { // TODO: In the future optimize this by using g_variant_get_child() // directly to avoid allocating a GVariant. self.try_child_value(index).map(|v| v.try_get()).transpose() } // rustdoc-stripper-ignore-next /// Read 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. /// * if the expected variant type does not match pub fn child_get(&self, index: usize) -> T { // TODO: In the future optimize this by using g_variant_get_child() // directly to avoid allocating a GVariant. self.child_value(index).get().unwrap() } // rustdoc-stripper-ignore-next /// Tries to extract a `&str`. /// /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type /// strings). #[doc(alias = "get_str")] #[doc(alias = "g_variant_get_string")] pub fn str(&self) -> Option<&str> { unsafe { match self.type_().as_str() { "s" | "o" | "g" => { let mut len = 0; let ptr = ffi::g_variant_get_string(self.to_glib_none().0, &mut len); if len == 0 { Some("") } else { let ret = str::from_utf8_unchecked(slice::from_raw_parts( ptr as *const u8, len as _, )); Some(ret) } } _ => None, } } } // rustdoc-stripper-ignore-next /// Tries to extract a `&[T]` from a variant of array type with a suitable element type. /// /// Returns an error if the type is wrong. #[doc(alias = "g_variant_get_fixed_array")] pub fn fixed_array(&self) -> Result<&[T], VariantTypeMismatchError> { unsafe { let expected_ty = T::static_variant_type().as_array(); if self.type_() != expected_ty { return Err(VariantTypeMismatchError { actual: self.type_().to_owned(), expected: expected_ty.into_owned(), }); } let mut n_elements = mem::MaybeUninit::uninit(); let ptr = ffi::g_variant_get_fixed_array( self.to_glib_none().0, n_elements.as_mut_ptr(), mem::size_of::(), ); let n_elements = n_elements.assume_init(); if n_elements == 0 { Ok(&[]) } else { debug_assert!(!ptr.is_null()); Ok(slice::from_raw_parts(ptr as *const T, n_elements)) } } } // rustdoc-stripper-ignore-next /// Creates a new Variant array from children. /// /// # Panics /// /// This function panics if not all variants are of type `T`. #[doc(alias = "g_variant_new_array")] pub fn array_from_iter( children: impl IntoIterator, ) -> Self { Self::array_from_iter_with_type(&T::static_variant_type(), children) } // rustdoc-stripper-ignore-next /// Creates a new Variant array from children with the specified type. /// /// # Panics /// /// This function panics if not all variants are of type `type_`. #[doc(alias = "g_variant_new_array")] pub fn array_from_iter_with_type( type_: &VariantTy, children: impl IntoIterator>, ) -> Self { unsafe { let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), type_.as_array().to_glib_none().0); let mut builder = builder.assume_init(); for value in children.into_iter() { let value = value.as_ref(); if ffi::g_variant_is_of_type(value.to_glib_none().0, type_.to_glib_none().0) == ffi::GFALSE { ffi::g_variant_builder_clear(&mut builder); assert!(value.is_type(type_)); } ffi::g_variant_builder_add_value(&mut builder, value.to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } // rustdoc-stripper-ignore-next /// Creates a new Variant array from a fixed array. #[doc(alias = "g_variant_new_fixed_array")] pub fn array_from_fixed_array(array: &[T]) -> Self { let type_ = T::static_variant_type(); unsafe { from_glib_none(ffi::g_variant_new_fixed_array( type_.as_ptr(), array.as_ptr() as ffi::gconstpointer, array.len(), mem::size_of::(), )) } } // rustdoc-stripper-ignore-next /// Creates a new Variant tuple from children. #[doc(alias = "g_variant_new_tuple")] pub fn tuple_from_iter(children: impl IntoIterator>) -> Self { unsafe { let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::TUPLE.to_glib_none().0); let mut builder = builder.assume_init(); for value in children.into_iter() { ffi::g_variant_builder_add_value(&mut builder, value.as_ref().to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } // rustdoc-stripper-ignore-next /// Creates a new dictionary entry Variant. /// /// [DictEntry] should be preferred over this when the types are known statically. #[doc(alias = "g_variant_new_dict_entry")] pub fn from_dict_entry(key: &Variant, value: &Variant) -> Self { unsafe { from_glib_none(ffi::g_variant_new_dict_entry( key.to_glib_none().0, value.to_glib_none().0, )) } } // rustdoc-stripper-ignore-next /// Creates a new maybe Variant. #[doc(alias = "g_variant_new_maybe")] pub fn from_maybe(child: Option<&Variant>) -> Self { let type_ = T::static_variant_type(); match child { Some(child) => { assert_eq!(type_, child.type_()); Self::from_some(child) } None => Self::from_none(&type_), } } // rustdoc-stripper-ignore-next /// Creates a new maybe Variant from a child. #[doc(alias = "g_variant_new_maybe")] pub fn from_some(child: &Variant) -> Self { unsafe { from_glib_none(ffi::g_variant_new_maybe( ptr::null(), child.to_glib_none().0, )) } } // rustdoc-stripper-ignore-next /// Creates a new maybe Variant with Nothing. #[doc(alias = "g_variant_new_maybe")] pub fn from_none(type_: &VariantTy) -> Self { unsafe { from_glib_none(ffi::g_variant_new_maybe( type_.to_glib_none().0, ptr::null_mut(), )) } } // rustdoc-stripper-ignore-next /// Extract the value of a maybe Variant. /// /// Returns the child value, or `None` if the value is Nothing. /// /// # Panics /// /// Panics if the variant is not maybe-typed. #[inline] pub fn as_maybe(&self) -> Option { assert!(self.type_().is_maybe()); unsafe { from_glib_full(ffi::g_variant_get_maybe(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Pretty-print the contents of this variant in a human-readable form. /// /// A variant can be recreated from this output via [`Variant::parse`]. #[doc(alias = "g_variant_print")] pub fn print(&self, type_annotate: bool) -> crate::GString { unsafe { from_glib_full(ffi::g_variant_print( self.to_glib_none().0, type_annotate.into_glib(), )) } } // rustdoc-stripper-ignore-next /// Parses a GVariant from the text representation produced by [`print()`](Self::print). #[doc(alias = "g_variant_parse")] pub fn parse(type_: Option<&VariantTy>, text: &str) -> Result { unsafe { let mut error = ptr::null_mut(); let text = text.as_bytes().as_ptr_range(); let variant = ffi::g_variant_parse( type_.to_glib_none().0, text.start as *const _, text.end as *const _, ptr::null_mut(), &mut error, ); if variant.is_null() { debug_assert!(!error.is_null()); Err(from_glib_full(error)) } else { debug_assert!(error.is_null()); Ok(from_glib_full(variant)) } } } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance. #[doc(alias = "g_variant_new_from_bytes")] pub fn from_bytes(bytes: &Bytes) -> Self { Variant::from_bytes_with_type(bytes, &T::static_variant_type()) } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance. /// /// This is the same as `from_bytes`, except that checks on the passed /// data are skipped. /// /// You should not use this function on data from external sources. /// /// # Safety /// /// Since the data is not validated, this is potentially dangerous if called /// on bytes which are not guaranteed to have come from serialising another /// Variant. The caller is responsible for ensuring bad data is not passed in. pub unsafe fn from_bytes_trusted(bytes: &Bytes) -> Self { Variant::from_bytes_with_type_trusted(bytes, &T::static_variant_type()) } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance. #[doc(alias = "g_variant_new_from_data")] pub fn from_data>(data: A) -> Self { Variant::from_data_with_type(data, &T::static_variant_type()) } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance. /// /// This is the same as `from_data`, except that checks on the passed /// data are skipped. /// /// You should not use this function on data from external sources. /// /// # Safety /// /// Since the data is not validated, this is potentially dangerous if called /// on bytes which are not guaranteed to have come from serialising another /// Variant. The caller is responsible for ensuring bad data is not passed in. pub unsafe fn from_data_trusted>(data: A) -> Self { Variant::from_data_with_type_trusted(data, &T::static_variant_type()) } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance with a given type. #[doc(alias = "g_variant_new_from_bytes")] pub fn from_bytes_with_type(bytes: &Bytes, type_: &VariantTy) -> Self { unsafe { from_glib_none(ffi::g_variant_new_from_bytes( type_.as_ptr() as *const _, bytes.to_glib_none().0, false.into_glib(), )) } } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance with a given type. /// /// This is the same as `from_bytes`, except that checks on the passed /// data are skipped. /// /// You should not use this function on data from external sources. /// /// # Safety /// /// Since the data is not validated, this is potentially dangerous if called /// on bytes which are not guaranteed to have come from serialising another /// Variant. The caller is responsible for ensuring bad data is not passed in. pub unsafe fn from_bytes_with_type_trusted(bytes: &Bytes, type_: &VariantTy) -> Self { from_glib_none(ffi::g_variant_new_from_bytes( type_.as_ptr() as *const _, bytes.to_glib_none().0, true.into_glib(), )) } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance with a given type. #[doc(alias = "g_variant_new_from_data")] pub fn from_data_with_type>(data: A, type_: &VariantTy) -> Self { unsafe { let data = Box::new(data); let (data_ptr, len) = { let data = (*data).as_ref(); (data.as_ptr(), data.len()) }; unsafe extern "C" fn free_data>(ptr: ffi::gpointer) { let _ = Box::from_raw(ptr as *mut A); } from_glib_none(ffi::g_variant_new_from_data( type_.as_ptr() as *const _, data_ptr as ffi::gconstpointer, len, false.into_glib(), Some(free_data::), Box::into_raw(data) as ffi::gpointer, )) } } // rustdoc-stripper-ignore-next /// Constructs a new serialized-mode GVariant instance with a given type. /// /// This is the same as `from_data`, except that checks on the passed /// data are skipped. /// /// You should not use this function on data from external sources. /// /// # Safety /// /// Since the data is not validated, this is potentially dangerous if called /// on bytes which are not guaranteed to have come from serialising another /// Variant. The caller is responsible for ensuring bad data is not passed in. pub unsafe fn from_data_with_type_trusted>(data: A, type_: &VariantTy) -> Self { let data = Box::new(data); let (data_ptr, len) = { let data = (*data).as_ref(); (data.as_ptr(), data.len()) }; unsafe extern "C" fn free_data>(ptr: ffi::gpointer) { let _ = Box::from_raw(ptr as *mut A); } from_glib_none(ffi::g_variant_new_from_data( type_.as_ptr() as *const _, data_ptr as ffi::gconstpointer, len, true.into_glib(), Some(free_data::), Box::into_raw(data) as ffi::gpointer, )) } // rustdoc-stripper-ignore-next /// Returns the serialized form of a GVariant instance. #[doc(alias = "get_data_as_bytes")] #[doc(alias = "g_variant_get_data_as_bytes")] pub fn data_as_bytes(&self) -> Bytes { unsafe { from_glib_full(ffi::g_variant_get_data_as_bytes(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Returns the serialized form of a GVariant instance. #[doc(alias = "g_variant_get_data")] pub fn data(&self) -> &[u8] { unsafe { let selfv = self.to_glib_none(); let len = ffi::g_variant_get_size(selfv.0); if len == 0 { return &[]; } let ptr = ffi::g_variant_get_data(selfv.0); slice::from_raw_parts(ptr as *const _, len as _) } } // rustdoc-stripper-ignore-next /// Returns the size of serialized form of a GVariant instance. #[doc(alias = "g_variant_get_size")] pub fn size(&self) -> usize { unsafe { ffi::g_variant_get_size(self.to_glib_none().0) } } // rustdoc-stripper-ignore-next /// Stores the serialized form of a GVariant instance into the given slice. /// /// The slice needs to be big enough. #[doc(alias = "g_variant_store")] pub fn store(&self, data: &mut [u8]) -> Result { unsafe { let size = ffi::g_variant_get_size(self.to_glib_none().0); if data.len() < size { return Err(bool_error!("Provided slice is too small")); } ffi::g_variant_store(self.to_glib_none().0, data.as_mut_ptr() as ffi::gpointer); Ok(size) } } // rustdoc-stripper-ignore-next /// Returns a copy of the variant in normal form. #[doc(alias = "g_variant_get_normal_form")] #[must_use] pub fn normal_form(&self) -> Self { unsafe { from_glib_full(ffi::g_variant_get_normal_form(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Returns a copy of the variant in the opposite endianness. #[doc(alias = "g_variant_byteswap")] #[must_use] pub fn byteswap(&self) -> Self { unsafe { from_glib_full(ffi::g_variant_byteswap(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Determines the number of children in a container GVariant instance. #[doc(alias = "g_variant_n_children")] pub fn n_children(&self) -> usize { assert!(self.is_container()); unsafe { ffi::g_variant_n_children(self.to_glib_none().0) } } // rustdoc-stripper-ignore-next /// Create an iterator over items in the variant. /// /// Note that this heap allocates a variant for each element, /// which can be particularly expensive for large arrays. pub fn iter(&self) -> VariantIter { assert!(self.is_container()); VariantIter::new(self.clone()) } // rustdoc-stripper-ignore-next /// Create an iterator over borrowed strings from a GVariant of type `as` (array of string). /// /// This will fail if the variant is not an array of with /// the expected child type. /// /// A benefit of this API over [`Self::iter()`] is that it /// minimizes allocation, and provides strongly typed access. /// /// ``` /// # use glib::prelude::*; /// let strs = &["foo", "bar"]; /// let strs_variant: glib::Variant = strs.to_variant(); /// for s in strs_variant.array_iter_str()? { /// println!("{}", s); /// } /// # Ok::<(), Box>(()) /// ``` pub fn array_iter_str(&self) -> Result { let child_ty = String::static_variant_type(); let actual_ty = self.type_(); let expected_ty = child_ty.as_array(); if actual_ty != expected_ty { return Err(VariantTypeMismatchError { actual: actual_ty.to_owned(), expected: expected_ty.into_owned(), }); } Ok(VariantStrIter::new(self)) } // rustdoc-stripper-ignore-next /// Return whether this Variant is a container type. #[doc(alias = "g_variant_is_container")] pub fn is_container(&self) -> bool { unsafe { from_glib(ffi::g_variant_is_container(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Return whether this Variant is in normal form. #[doc(alias = "g_variant_is_normal_form")] pub fn is_normal_form(&self) -> bool { unsafe { from_glib(ffi::g_variant_is_normal_form(self.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Return whether input string is a valid `VariantClass::ObjectPath`. #[doc(alias = "g_variant_is_object_path")] pub fn is_object_path(string: &str) -> bool { unsafe { from_glib(ffi::g_variant_is_object_path(string.to_glib_none().0)) } } // rustdoc-stripper-ignore-next /// Return whether input string is a valid `VariantClass::Signature`. #[doc(alias = "g_variant_is_signature")] pub fn is_signature(string: &str) -> bool { unsafe { from_glib(ffi::g_variant_is_signature(string.to_glib_none().0)) } } } unsafe impl Send for Variant {} unsafe impl Sync for Variant {} impl fmt::Debug for Variant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Variant") .field("ptr", &ToGlibPtr::<*const _>::to_glib_none(self).0) .field("type", &self.type_()) .field("value", &self.to_string()) .finish() } } impl fmt::Display for Variant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.print(true)) } } impl str::FromStr for Variant { type Err = crate::Error; fn from_str(s: &str) -> Result { Self::parse(None, s) } } impl PartialEq for Variant { #[doc(alias = "g_variant_equal")] fn eq(&self, other: &Self) -> bool { unsafe { from_glib(ffi::g_variant_equal( ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _, )) } } } impl Eq for Variant {} impl PartialOrd for Variant { fn partial_cmp(&self, other: &Self) -> Option { unsafe { if ffi::g_variant_classify(self.to_glib_none().0) != ffi::g_variant_classify(other.to_glib_none().0) { return None; } if self.is_container() { return None; } let res = ffi::g_variant_compare( ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _, ); Some(res.cmp(&0)) } } } impl Hash for Variant { #[doc(alias = "g_variant_hash")] fn hash(&self, state: &mut H) { unsafe { state.write_u32(ffi::g_variant_hash( ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, )) } } } impl AsRef for Variant { #[inline] fn as_ref(&self) -> &Self { self } } // rustdoc-stripper-ignore-next /// Converts to `Variant`. pub trait ToVariant { // rustdoc-stripper-ignore-next /// Returns a `Variant` clone of `self`. fn to_variant(&self) -> Variant; } // rustdoc-stripper-ignore-next /// Extracts a value. pub trait FromVariant: Sized + StaticVariantType { // rustdoc-stripper-ignore-next /// Tries to extract a value. /// /// Returns `Some` if the variant's type matches `Self`. fn from_variant(variant: &Variant) -> Option; } // rustdoc-stripper-ignore-next /// Returns `VariantType` of `Self`. pub trait StaticVariantType { // rustdoc-stripper-ignore-next /// Returns the `VariantType` corresponding to `Self`. fn static_variant_type() -> Cow<'static, VariantTy>; } impl StaticVariantType for Variant { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed(VariantTy::VARIANT) } } impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T { fn to_variant(&self) -> Variant { ::to_variant(self) } } impl<'a, T: ?Sized + Into + Clone> From<&'a T> for Variant { #[inline] fn from(v: &'a T) -> Self { v.clone().into() } } impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T { fn static_variant_type() -> Cow<'static, VariantTy> { ::static_variant_type() } } macro_rules! impl_numeric { ($name:ty, $typ:expr, $new_fn:ident, $get_fn:ident) => { impl StaticVariantType for $name { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed($typ) } } impl ToVariant for $name { fn to_variant(&self) -> Variant { unsafe { from_glib_none(ffi::$new_fn(*self)) } } } impl From<$name> for Variant { #[inline] fn from(v: $name) -> Self { v.to_variant() } } impl FromVariant for $name { fn from_variant(variant: &Variant) -> Option { unsafe { if variant.is::() { Some(ffi::$get_fn(variant.to_glib_none().0)) } else { None } } } } }; } impl_numeric!(u8, VariantTy::BYTE, g_variant_new_byte, g_variant_get_byte); impl_numeric!( i16, VariantTy::INT16, g_variant_new_int16, g_variant_get_int16 ); impl_numeric!( u16, VariantTy::UINT16, g_variant_new_uint16, g_variant_get_uint16 ); impl_numeric!( i32, VariantTy::INT32, g_variant_new_int32, g_variant_get_int32 ); impl_numeric!( u32, VariantTy::UINT32, g_variant_new_uint32, g_variant_get_uint32 ); impl_numeric!( i64, VariantTy::INT64, g_variant_new_int64, g_variant_get_int64 ); impl_numeric!( u64, VariantTy::UINT64, g_variant_new_uint64, g_variant_get_uint64 ); impl_numeric!( f64, VariantTy::DOUBLE, g_variant_new_double, g_variant_get_double ); impl StaticVariantType for () { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed(VariantTy::UNIT) } } impl ToVariant for () { fn to_variant(&self) -> Variant { unsafe { from_glib_none(ffi::g_variant_new_tuple(ptr::null(), 0)) } } } impl From<()> for Variant { #[inline] fn from(_: ()) -> Self { ().to_variant() } } impl FromVariant for () { fn from_variant(variant: &Variant) -> Option { if variant.is::() { Some(()) } else { None } } } impl StaticVariantType for bool { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed(VariantTy::BOOLEAN) } } impl ToVariant for bool { fn to_variant(&self) -> Variant { unsafe { from_glib_none(ffi::g_variant_new_boolean(self.into_glib())) } } } impl From for Variant { #[inline] fn from(v: bool) -> Self { v.to_variant() } } impl FromVariant for bool { fn from_variant(variant: &Variant) -> Option { unsafe { if variant.is::() { Some(from_glib(ffi::g_variant_get_boolean( variant.to_glib_none().0, ))) } else { None } } } } impl StaticVariantType for String { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed(VariantTy::STRING) } } impl ToVariant for String { fn to_variant(&self) -> Variant { self[..].to_variant() } } impl From for Variant { #[inline] fn from(s: String) -> Self { s.to_variant() } } impl FromVariant for String { fn from_variant(variant: &Variant) -> Option { variant.str().map(String::from) } } impl StaticVariantType for str { fn static_variant_type() -> Cow<'static, VariantTy> { String::static_variant_type() } } impl ToVariant for str { fn to_variant(&self) -> Variant { unsafe { from_glib_none(ffi::g_variant_new_take_string(self.to_glib_full())) } } } impl From<&str> for Variant { #[inline] fn from(s: &str) -> Self { s.to_variant() } } impl StaticVariantType for std::path::PathBuf { fn static_variant_type() -> Cow<'static, VariantTy> { std::path::Path::static_variant_type() } } impl ToVariant for std::path::PathBuf { fn to_variant(&self) -> Variant { self.as_path().to_variant() } } impl From for Variant { #[inline] fn from(p: std::path::PathBuf) -> Self { p.to_variant() } } impl FromVariant for std::path::PathBuf { fn from_variant(variant: &Variant) -> Option { unsafe { let ptr = ffi::g_variant_get_bytestring(variant.to_glib_none().0); Some(crate::translate::c_to_path_buf(ptr as *const _)) } } } impl StaticVariantType for std::path::Path { fn static_variant_type() -> Cow<'static, VariantTy> { <&[u8]>::static_variant_type() } } impl ToVariant for std::path::Path { fn to_variant(&self) -> Variant { let tmp = crate::translate::path_to_c(self); unsafe { from_glib_none(ffi::g_variant_new_bytestring(tmp.as_ptr() as *const u8)) } } } impl From<&std::path::Path> for Variant { #[inline] fn from(p: &std::path::Path) -> Self { p.to_variant() } } impl StaticVariantType for std::ffi::OsString { fn static_variant_type() -> Cow<'static, VariantTy> { std::ffi::OsStr::static_variant_type() } } impl ToVariant for std::ffi::OsString { fn to_variant(&self) -> Variant { self.as_os_str().to_variant() } } impl From for Variant { #[inline] fn from(s: std::ffi::OsString) -> Self { s.to_variant() } } impl FromVariant for std::ffi::OsString { fn from_variant(variant: &Variant) -> Option { unsafe { let ptr = ffi::g_variant_get_bytestring(variant.to_glib_none().0); Some(crate::translate::c_to_os_string(ptr as *const _)) } } } impl StaticVariantType for std::ffi::OsStr { fn static_variant_type() -> Cow<'static, VariantTy> { <&[u8]>::static_variant_type() } } impl ToVariant for std::ffi::OsStr { fn to_variant(&self) -> Variant { let tmp = crate::translate::os_str_to_c(self); unsafe { from_glib_none(ffi::g_variant_new_bytestring(tmp.as_ptr() as *const u8)) } } } impl From<&std::ffi::OsStr> for Variant { #[inline] fn from(s: &std::ffi::OsStr) -> Self { s.to_variant() } } impl StaticVariantType for Option { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Owned(VariantType::new_maybe(&T::static_variant_type())) } } impl ToVariant for Option { fn to_variant(&self) -> Variant { Variant::from_maybe::(self.as_ref().map(|m| m.to_variant()).as_ref()) } } impl> From> for Variant { #[inline] fn from(v: Option) -> Self { Variant::from_maybe::(v.map(|v| v.into()).as_ref()) } } impl FromVariant for Option { fn from_variant(variant: &Variant) -> Option { unsafe { if variant.is::() { let c_child = ffi::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> { T::static_variant_type().as_array() } } impl ToVariant for [T] { fn to_variant(&self) -> Variant { unsafe { if self.is_empty() { return from_glib_none(ffi::g_variant_new_array( T::static_variant_type().to_glib_none().0, ptr::null(), 0, )); } let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::ARRAY.to_glib_none().0); let mut builder = builder.assume_init(); for value in self { let value = value.to_variant(); ffi::g_variant_builder_add_value(&mut builder, value.to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } impl From<&[T]> for Variant { #[inline] fn from(s: &[T]) -> Self { s.to_variant() } } impl FromVariant for Vec { fn from_variant(variant: &Variant) -> Option { if !variant.is_container() { return None; } let mut vec = Vec::with_capacity(variant.n_children()); for i in 0..variant.n_children() { match variant.child_value(i).get() { Some(child) => vec.push(child), None => return None, } } Some(vec) } } impl ToVariant for Vec { fn to_variant(&self) -> Variant { self.as_slice().to_variant() } } impl> From> for Variant { fn from(v: Vec) -> Self { unsafe { if v.is_empty() { return from_glib_none(ffi::g_variant_new_array( T::static_variant_type().to_glib_none().0, ptr::null(), 0, )); } let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::ARRAY.to_glib_none().0); let mut builder = builder.assume_init(); for value in v { let value = value.into(); ffi::g_variant_builder_add_value(&mut builder, value.to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } impl StaticVariantType for Vec { fn static_variant_type() -> Cow<'static, VariantTy> { <[T]>::static_variant_type() } } impl FromVariant for HashMap where K: FromVariant + Eq + Hash, V: FromVariant, H: BuildHasher + Default, { fn from_variant(variant: &Variant) -> Option { if !variant.is_container() { return None; } let mut map = HashMap::default(); for i in 0..variant.n_children() { let entry = variant.child_value(i); let key = match entry.child_value(0).get() { Some(key) => key, None => return None, }; let val = match entry.child_value(1).get() { Some(val) => val, None => return None, }; map.insert(key, val); } Some(map) } } impl FromVariant for BTreeMap where K: FromVariant + Eq + Ord, V: FromVariant, { fn from_variant(variant: &Variant) -> Option { if !variant.is_container() { return None; } let mut map = BTreeMap::default(); for i in 0..variant.n_children() { let entry = variant.child_value(i); let key = match entry.child_value(0).get() { Some(key) => key, None => return None, }; let val = match entry.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 { unsafe { if self.is_empty() { return from_glib_none(ffi::g_variant_new_array( DictEntry::::static_variant_type().to_glib_none().0, ptr::null(), 0, )); } let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::ARRAY.to_glib_none().0); let mut builder = builder.assume_init(); for (key, value) in self { let entry = DictEntry::new(key, value).to_variant(); ffi::g_variant_builder_add_value(&mut builder, entry.to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } impl From> for Variant where K: StaticVariantType + Into + Eq + Hash, V: StaticVariantType + Into, { fn from(m: HashMap) -> Self { unsafe { if m.is_empty() { return from_glib_none(ffi::g_variant_new_array( DictEntry::::static_variant_type().to_glib_none().0, ptr::null(), 0, )); } let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::ARRAY.to_glib_none().0); let mut builder = builder.assume_init(); for (key, value) in m { let entry = Variant::from(DictEntry::new(key, value)); ffi::g_variant_builder_add_value(&mut builder, entry.to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } impl ToVariant for BTreeMap where K: StaticVariantType + ToVariant + Eq + Hash, V: StaticVariantType + ToVariant, { fn to_variant(&self) -> Variant { unsafe { if self.is_empty() { return from_glib_none(ffi::g_variant_new_array( DictEntry::::static_variant_type().to_glib_none().0, ptr::null(), 0, )); } let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::ARRAY.to_glib_none().0); let mut builder = builder.assume_init(); for (key, value) in self { let entry = DictEntry::new(key, value).to_variant(); ffi::g_variant_builder_add_value(&mut builder, entry.to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } impl From> for Variant where K: StaticVariantType + Into + Eq + Hash, V: StaticVariantType + Into, { fn from(m: BTreeMap) -> Self { unsafe { if m.is_empty() { return from_glib_none(ffi::g_variant_new_array( DictEntry::::static_variant_type().to_glib_none().0, ptr::null(), 0, )); } let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::ARRAY.to_glib_none().0); let mut builder = builder.assume_init(); for (key, value) in m { let entry = Variant::from(DictEntry::new(key, value)); ffi::g_variant_builder_add_value(&mut builder, entry.to_glib_none().0); } from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } /// 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 = [ /// DictEntry::new("uuid", 1000u32), /// DictEntry::new("guid", 1001u32), /// ]; /// let dict = entries.into_iter().collect::(); /// assert_eq!(dict.n_children(), 2); /// assert_eq!(dict.type_().as_str(), "a{su}"); /// ``` pub struct DictEntry { key: K, value: V, } impl DictEntry where K: StaticVariantType, V: StaticVariantType, { pub fn new(key: K, value: V) -> Self { Self { key, value } } pub fn key(&self) -> &K { &self.key } pub fn value(&self) -> &V { &self.value } } impl FromVariant for DictEntry where K: FromVariant, V: FromVariant, { fn from_variant(variant: &Variant) -> Option { if !variant.type_().is_subtype_of(VariantTy::DICT_ENTRY) { return None; } let key = match variant.child_value(0).get() { Some(key) => key, None => return None, }; let value = match variant.child_value(1).get() { Some(value) => value, None => return None, }; Some(Self { key, value }) } } impl ToVariant for DictEntry where K: StaticVariantType + ToVariant, V: StaticVariantType + ToVariant, { fn to_variant(&self) -> Variant { Variant::from_dict_entry(&self.key.to_variant(), &self.value.to_variant()) } } impl From> for Variant where K: StaticVariantType + Into, V: StaticVariantType + Into, { fn from(e: DictEntry) -> Self { Variant::from_dict_entry(&e.key.into(), &e.value.into()) } } impl ToVariant for Variant { fn to_variant(&self) -> Variant { Variant::from_variant(self) } } impl FromVariant for Variant { fn from_variant(variant: &Variant) -> Option { variant.as_variant() } } impl StaticVariantType for DictEntry { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Owned(VariantType::new_dict_entry( &K::static_variant_type(), &V::static_variant_type(), )) } } fn static_variant_mapping() -> Cow<'static, VariantTy> where K: StaticVariantType, V: StaticVariantType, { use std::fmt::Write; let key_type = K::static_variant_type(); let value_type = V::static_variant_type(); if key_type == VariantTy::STRING && value_type == VariantTy::VARIANT { return Cow::Borrowed(VariantTy::VARDICT); } let mut builder = crate::GStringBuilder::default(); write!(builder, "a{{{}{}}}", key_type.as_str(), value_type.as_str()).unwrap(); Cow::Owned(VariantType::from_string(builder.into_string()).unwrap()) } impl StaticVariantType for HashMap where K: StaticVariantType, V: StaticVariantType, H: BuildHasher + Default, { fn static_variant_type() -> Cow<'static, VariantTy> { static_variant_mapping::() } } impl StaticVariantType for BTreeMap where K: StaticVariantType, V: StaticVariantType, { fn static_variant_type() -> Cow<'static, VariantTy> { static_variant_mapping::() } } 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> { Cow::Owned(VariantType::new_tuple(&[ $( $name::static_variant_type(), )+ ])) } } impl<$($name),+> FromVariant for ($($name,)+) where $($name: FromVariant,)+ { fn from_variant(variant: &Variant) -> Option { if !variant.type_().is_subtype_of(VariantTy::TUPLE) { return None; } Some(( $( match variant.try_child_get::<$name>($n) { Ok(Some(field)) => field, _ => return None, }, )+ )) } } impl<$($name),+> ToVariant for ($($name,)+) where $($name: ToVariant,)+ { fn to_variant(&self) -> Variant { unsafe { let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::TUPLE.to_glib_none().0); let mut builder = builder.assume_init(); $( let field = self.$n.to_variant(); ffi::g_variant_builder_add_value(&mut builder, field.to_glib_none().0); )+ from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } impl<$($name),+> From<($($name,)+)> for Variant where $($name: Into,)+ { fn from(t: ($($name,)+)) -> Self { unsafe { let mut builder = mem::MaybeUninit::uninit(); ffi::g_variant_builder_init(builder.as_mut_ptr(), VariantTy::TUPLE.to_glib_none().0); let mut builder = builder.assume_init(); $( let field = t.$n.into(); ffi::g_variant_builder_add_value(&mut builder, field.to_glib_none().0); )+ from_glib_none(ffi::g_variant_builder_end(&mut builder)) } } } )+ } } 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) } impl + StaticVariantType> FromIterator for Variant { fn from_iter>(iter: I) -> Self { Variant::array_from_iter::(iter.into_iter().map(|v| v.into())) } } /// Trait for fixed size variant types. pub unsafe trait FixedSizeVariantType: StaticVariantType + Sized + Copy {} unsafe impl FixedSizeVariantType for u8 {} unsafe impl FixedSizeVariantType for i16 {} unsafe impl FixedSizeVariantType for u16 {} unsafe impl FixedSizeVariantType for i32 {} unsafe impl FixedSizeVariantType for u32 {} unsafe impl FixedSizeVariantType for i64 {} unsafe impl FixedSizeVariantType for u64 {} unsafe impl FixedSizeVariantType for f64 {} unsafe impl FixedSizeVariantType for bool {} /// Wrapper type for fixed size type arrays. /// /// Converting this from/to a `Variant` is generally more efficient than working on the type /// directly. This is especially important when deriving `Variant` trait implementations on custom /// types. /// /// This wrapper type can hold for example `Vec`, `Box<[u8]>` and similar types. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FixedSizeVariantArray(A, std::marker::PhantomData) where A: AsRef<[T]>, T: FixedSizeVariantType; impl, T: FixedSizeVariantType> From for FixedSizeVariantArray { fn from(array: A) -> Self { FixedSizeVariantArray(array, std::marker::PhantomData) } } impl, T: FixedSizeVariantType> FixedSizeVariantArray { pub fn into_inner(self) -> A { self.0 } } impl, T: FixedSizeVariantType> std::ops::Deref for FixedSizeVariantArray { type Target = A; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl, T: FixedSizeVariantType> std::ops::DerefMut for FixedSizeVariantArray { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl, T: FixedSizeVariantType> AsRef for FixedSizeVariantArray { #[inline] fn as_ref(&self) -> &A { &self.0 } } impl, T: FixedSizeVariantType> AsMut for FixedSizeVariantArray { #[inline] fn as_mut(&mut self) -> &mut A { &mut self.0 } } impl, T: FixedSizeVariantType> AsRef<[T]> for FixedSizeVariantArray { #[inline] fn as_ref(&self) -> &[T] { self.0.as_ref() } } impl + AsMut<[T]>, T: FixedSizeVariantType> AsMut<[T]> for FixedSizeVariantArray { #[inline] fn as_mut(&mut self) -> &mut [T] { self.0.as_mut() } } impl, T: FixedSizeVariantType> StaticVariantType for FixedSizeVariantArray { fn static_variant_type() -> Cow<'static, VariantTy> { <[T]>::static_variant_type() } } impl + for<'a> From<&'a [T]>, T: FixedSizeVariantType> FromVariant for FixedSizeVariantArray { fn from_variant(variant: &Variant) -> Option { Some(FixedSizeVariantArray( A::from(variant.fixed_array::().ok()?), std::marker::PhantomData, )) } } impl, T: FixedSizeVariantType> ToVariant for FixedSizeVariantArray { fn to_variant(&self) -> Variant { Variant::array_from_fixed_array(self.0.as_ref()) } } impl, T: FixedSizeVariantType> From> for Variant { #[doc(alias = "g_variant_new_from_data")] fn from(a: FixedSizeVariantArray) -> Self { unsafe { let data = Box::new(a.0); let (data_ptr, len) = { let data = (*data).as_ref(); (data.as_ptr(), mem::size_of_val(data)) }; unsafe extern "C" fn free_data, T: FixedSizeVariantType>( ptr: ffi::gpointer, ) { let _ = Box::from_raw(ptr as *mut A); } from_glib_none(ffi::g_variant_new_from_data( T::static_variant_type().to_glib_none().0, data_ptr as ffi::gconstpointer, len, false.into_glib(), Some(free_data::), Box::into_raw(data) as ffi::gpointer, )) } } } /// A wrapper type around `Variant` handles. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Handle(pub i32); impl From for Handle { fn from(v: i32) -> Self { Handle(v) } } impl From for i32 { fn from(v: Handle) -> Self { v.0 } } impl StaticVariantType for Handle { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed(VariantTy::HANDLE) } } impl ToVariant for Handle { fn to_variant(&self) -> Variant { unsafe { from_glib_none(ffi::g_variant_new_handle(self.0)) } } } impl From for Variant { #[inline] fn from(h: Handle) -> Self { h.to_variant() } } impl FromVariant for Handle { fn from_variant(variant: &Variant) -> Option { unsafe { if variant.is::() { Some(Handle(ffi::g_variant_get_handle(variant.to_glib_none().0))) } else { None } } } } /// A wrapper type around `Variant` object paths. /// /// Values of these type are guaranteed to be valid object paths. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ObjectPath(String); impl ObjectPath { pub fn as_str(&self) -> &str { &self.0 } } impl std::ops::Deref for ObjectPath { type Target = str; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl TryFrom for ObjectPath { type Error = crate::BoolError; fn try_from(v: String) -> Result { if !Variant::is_object_path(&v) { return Err(bool_error!("Invalid object path")); } Ok(ObjectPath(v)) } } impl<'a> TryFrom<&'a str> for ObjectPath { type Error = crate::BoolError; fn try_from(v: &'a str) -> Result { ObjectPath::try_from(String::from(v)) } } impl From for String { fn from(v: ObjectPath) -> Self { v.0 } } impl StaticVariantType for ObjectPath { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed(VariantTy::OBJECT_PATH) } } impl ToVariant for ObjectPath { fn to_variant(&self) -> Variant { unsafe { from_glib_none(ffi::g_variant_new_object_path(self.0.to_glib_none().0)) } } } impl From for Variant { #[inline] fn from(p: ObjectPath) -> Self { let mut s = p.0; s.push('\0'); unsafe { Self::from_data_trusted::(s) } } } impl FromVariant for ObjectPath { #[allow(unused_unsafe)] fn from_variant(variant: &Variant) -> Option { unsafe { if variant.is::() { Some(ObjectPath(String::from(variant.str().unwrap()))) } else { None } } } } /// A wrapper type around `Variant` signatures. /// /// Values of these type are guaranteed to be valid signatures. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Signature(String); impl Signature { pub fn as_str(&self) -> &str { &self.0 } } impl std::ops::Deref for Signature { type Target = str; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl TryFrom for Signature { type Error = crate::BoolError; fn try_from(v: String) -> Result { if !Variant::is_signature(&v) { return Err(bool_error!("Invalid signature")); } Ok(Signature(v)) } } impl<'a> TryFrom<&'a str> for Signature { type Error = crate::BoolError; fn try_from(v: &'a str) -> Result { Signature::try_from(String::from(v)) } } impl From for String { fn from(v: Signature) -> Self { v.0 } } impl StaticVariantType for Signature { fn static_variant_type() -> Cow<'static, VariantTy> { Cow::Borrowed(VariantTy::SIGNATURE) } } impl ToVariant for Signature { fn to_variant(&self) -> Variant { unsafe { from_glib_none(ffi::g_variant_new_signature(self.0.to_glib_none().0)) } } } impl From for Variant { #[inline] fn from(s: Signature) -> Self { let mut s = s.0; s.push('\0'); unsafe { Self::from_data_trusted::(s) } } } impl FromVariant for Signature { #[allow(unused_unsafe)] fn from_variant(variant: &Variant) -> Option { unsafe { if variant.is::() { Some(Signature(String::from(variant.str().unwrap()))) } else { None } } } } #[cfg(test)] mod tests { use std::collections::{HashMap, HashSet}; use super::*; macro_rules! unsigned { ($name:ident, $ty:ident) => { #[test] fn $name() { let mut n = $ty::max_value(); while n > 0 { let v = n.to_variant(); assert_eq!(v.get(), Some(n)); n /= 2; } } }; } macro_rules! signed { ($name:ident, $ty:ident) => { #[test] fn $name() { let mut n = $ty::max_value(); while n > 0 { let v = n.to_variant(); assert_eq!(v.get(), Some(n)); let v = (-n).to_variant(); assert_eq!(v.get(), Some(-n)); n /= 2; } } }; } unsigned!(test_u8, u8); unsigned!(test_u16, u16); unsigned!(test_u32, u32); unsigned!(test_u64, u64); signed!(test_i16, i16); signed!(test_i32, i32); signed!(test_i64, i64); #[test] fn test_str() { let s = "this is a test"; let v = s.to_variant(); assert_eq!(v.str(), Some(s)); assert_eq!(42u32.to_variant().str(), None); } #[test] fn test_fixed_array() { let b = b"this is a test"; let v = Variant::array_from_fixed_array(&b[..]); assert_eq!(v.type_().as_str(), "ay"); assert_eq!(v.fixed_array::().unwrap(), b); assert!(42u32.to_variant().fixed_array::().is_err()); let b = [1u32, 10u32, 100u32]; let v = Variant::array_from_fixed_array(&b); assert_eq!(v.type_().as_str(), "au"); assert_eq!(v.fixed_array::().unwrap(), b); assert!(v.fixed_array::().is_err()); let b = [true, false, true]; let v = Variant::array_from_fixed_array(&b); assert_eq!(v.type_().as_str(), "ab"); assert_eq!(v.fixed_array::().unwrap(), b); assert!(v.fixed_array::().is_err()); let b = [1.0f64, 2.0f64, 3.0f64]; let v = Variant::array_from_fixed_array(&b); assert_eq!(v.type_().as_str(), "ad"); #[allow(clippy::float_cmp)] { assert_eq!(v.fixed_array::().unwrap(), b); } assert!(v.fixed_array::().is_err()); } #[test] fn test_fixed_variant_array() { let b = FixedSizeVariantArray::from(&b"this is a test"[..]); let v = b.to_variant(); assert_eq!(v.type_().as_str(), "ay"); assert_eq!( &*v.get::, u8>>().unwrap(), &*b ); let b = FixedSizeVariantArray::from(vec![1i32, 2, 3]); let v = b.to_variant(); assert_eq!(v.type_().as_str(), "ai"); assert_eq!(v.get::, i32>>().unwrap(), b); } #[test] fn test_string() { let s = String::from("this is a test"); let v = s.to_variant(); assert_eq!(v.get(), Some(s)); assert_eq!(v.normal_form(), v); } #[test] fn test_eq() { let v1 = "this is a test".to_variant(); let v2 = "this is a test".to_variant(); let v3 = "test".to_variant(); assert_eq!(v1, v2); assert_ne!(v1, v3); } #[test] fn test_hash() { let v1 = "this is a test".to_variant(); let v2 = "this is a test".to_variant(); let v3 = "test".to_variant(); let mut set = HashSet::new(); set.insert(v1); assert!(set.contains(&v2)); assert!(!set.contains(&v3)); assert_eq!( >::static_variant_type().as_str(), "a{s(syu)}" ); } #[test] fn test_array() { assert_eq!(>::static_variant_type().as_str(), "as"); assert_eq!( >::static_variant_type().as_str(), "a(syu)" ); let a = ["foo", "bar", "baz"].to_variant(); assert_eq!(a.normal_form(), a); assert_eq!(a.array_iter_str().unwrap().len(), 3); let o = 0u32.to_variant(); assert!(o.array_iter_str().is_err()); } #[test] fn test_array_from_iter() { let a = Variant::array_from_iter::( ["foo", "bar", "baz"].into_iter().map(|s| s.to_variant()), ); assert_eq!(a.type_().as_str(), "as"); assert_eq!(a.n_children(), 3); assert_eq!(a.try_child_get::(0), Ok(Some(String::from("foo")))); assert_eq!(a.try_child_get::(1), Ok(Some(String::from("bar")))); assert_eq!(a.try_child_get::(2), Ok(Some(String::from("baz")))); } #[test] fn test_array_collect() { let a = ["foo", "bar", "baz"].into_iter().collect::(); assert_eq!(a.type_().as_str(), "as"); assert_eq!(a.n_children(), 3); assert_eq!(a.try_child_get::(0), Ok(Some(String::from("foo")))); assert_eq!(a.try_child_get::(1), Ok(Some(String::from("bar")))); assert_eq!(a.try_child_get::(2), Ok(Some(String::from("baz")))); } #[test] fn test_tuple() { assert_eq!(<(&str, u32)>::static_variant_type().as_str(), "(su)"); assert_eq!(<(&str, u8, u32)>::static_variant_type().as_str(), "(syu)"); let a = ("test", 1u8, 2u32).to_variant(); assert_eq!(a.normal_form(), a); assert_eq!(a.try_child_get::(0), Ok(Some(String::from("test")))); assert_eq!(a.try_child_get::(1), Ok(Some(1u8))); assert_eq!(a.try_child_get::(2), Ok(Some(2u32))); assert_eq!( a.try_get::<(String, u8, u32)>(), Ok((String::from("test"), 1u8, 2u32)) ); } #[test] fn test_tuple_from_iter() { let a = Variant::tuple_from_iter(["foo".to_variant(), 1u8.to_variant(), 2i32.to_variant()]); assert_eq!(a.type_().as_str(), "(syi)"); assert_eq!(a.n_children(), 3); assert_eq!(a.try_child_get::(0), Ok(Some(String::from("foo")))); assert_eq!(a.try_child_get::(1), Ok(Some(1u8))); assert_eq!(a.try_child_get::(2), Ok(Some(2i32))); } #[test] fn test_empty() { assert_eq!(<()>::static_variant_type().as_str(), "()"); let a = ().to_variant(); assert_eq!(a.type_().as_str(), "()"); assert_eq!(a.get::<()>(), Some(())); } #[test] fn test_maybe() { assert!(>::static_variant_type().is_maybe()); let m1 = Some(()).to_variant(); assert_eq!(m1.type_().as_str(), "m()"); assert_eq!(m1.get::>(), Some(Some(()))); assert!(m1.as_maybe().is_some()); let m2 = None::<()>.to_variant(); assert!(m2.as_maybe().is_none()); } #[test] fn test_btreemap() { assert_eq!( >::static_variant_type().as_str(), "a{su}" ); // Validate that BTreeMap adds entries to dict in sorted order let mut m = BTreeMap::new(); let total = 20; for n in 0..total { let k = format!("v{n:04}"); m.insert(k, n as u32); } let v = m.to_variant(); let n = v.n_children(); assert_eq!(total, n); for n in 0..total { let child = v .try_child_get::>(n) .unwrap() .unwrap(); assert_eq!(*child.value(), n as u32); } assert_eq!(BTreeMap::from_variant(&v).unwrap(), m); } #[test] fn test_get() -> Result<(), Box> { let u = 42u32.to_variant(); assert!(u.get::().is_none()); assert_eq!(u.get::().unwrap(), 42); assert!(u.try_get::().is_err()); // Test ? conversion assert_eq!(u.try_get::()?, 42); Ok(()) } #[test] fn test_byteswap() { let u = 42u32.to_variant(); assert_eq!(u.byteswap().get::().unwrap(), 704643072u32); assert_eq!(u.byteswap().byteswap().get::().unwrap(), 42u32); } #[test] fn test_try_child() { let a = ["foo"].to_variant(); assert!(a.try_child_value(0).is_some()); assert_eq!(a.try_child_get::(0).unwrap().unwrap(), "foo"); assert_eq!(a.child_get::(0), "foo"); assert!(a.try_child_get::(0).is_err()); assert!(a.try_child_value(1).is_none()); assert!(a.try_child_get::(1).unwrap().is_none()); let u = 42u32.to_variant(); assert!(u.try_child_value(0).is_none()); assert!(u.try_child_get::(0).unwrap().is_none()); } #[test] fn test_serialize() { let a = ("test", 1u8, 2u32).to_variant(); let bytes = a.data_as_bytes(); let data = a.data(); let len = a.size(); assert_eq!(bytes.len(), len); assert_eq!(data.len(), len); let mut store_data = vec![0u8; len]; assert_eq!(a.store(&mut store_data).unwrap(), len); assert_eq!(&bytes, data); assert_eq!(&store_data, data); let b = Variant::from_data::<(String, u8, u32), _>(store_data); assert_eq!(a, b); let c = Variant::from_bytes::<(String, u8, u32)>(&bytes); assert_eq!(a, c); } #[test] fn test_print_parse() { let a = ("test", 1u8, 2u32).to_variant(); let a2 = Variant::parse(Some(a.type_()), &a.print(false)).unwrap(); assert_eq!(a, a2); let a3: Variant = a.to_string().parse().unwrap(); assert_eq!(a, a3); } #[cfg(any(unix, windows))] #[test] fn test_paths() { use std::path::PathBuf; let path = PathBuf::from("foo"); let v = path.to_variant(); assert_eq!(PathBuf::from_variant(&v), Some(path)); } #[test] fn test_regression_from_variant_panics() { let variant = "text".to_variant(); let hashmap: Option> = FromVariant::from_variant(&variant); assert!(hashmap.is_none()); let variant = HashMap::::new().to_variant(); let hashmap: Option> = FromVariant::from_variant(&variant); assert!(hashmap.is_some()); } }