Skip to content
This repository has been archived by the owner on Jun 8, 2021. It is now read-only.

Commit

Permalink
Add conversion between Variant and HashMap
Browse files Browse the repository at this point in the history
  • Loading branch information
zeenix committed Sep 1, 2020
1 parent 27ea510 commit b9d6948
Showing 1 changed file with 82 additions and 0 deletions.
82 changes: 82 additions & 0 deletions src/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -54,13 +55,24 @@
//! assert_eq!(variant.n_children(), 2);
//! let vec = <Vec<String>>::from_variant(&array).unwrap();
//! assert_eq!(vec[0], "Hello");
//!
//! // Conversion to and from HashMap is also possible
//! let mut map: HashMap<u16, &str> = HashMap::new();
//! map.insert(1, "hi");
//! map.insert(2, "there");
//! let variant = map.to_variant();
//! assert_eq!(variant.n_children(), 2);
//! let map: HashMap<u16, String> = HashMap::from_variant(&variant).unwrap();
//! assert_eq!(map[&1], "hi");
//! assert_eq!(map[&2], "there");
//! ```

use bytes::Bytes;
use glib_sys;
use gobject_sys;
use gstring::GString;
use std::borrow::Cow;
use std::collections::HashMap;
use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher};
Expand Down Expand Up @@ -238,6 +250,16 @@ impl Variant {
))
}

/// Creates a new dictionary entry GVariant.
pub fn new_dict_entry(key: &Variant, value: &Variant) -> Self {
unsafe {
from_glib_none(glib_sys::g_variant_new_dict_entry(
key.to_glib_none().0,
value.to_glib_none().0,
))
}
}

/// Returns the serialised form of a GVariant instance.
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)) }
Expand Down Expand Up @@ -496,6 +518,66 @@ impl<T: StaticVariantType> StaticVariantType for Vec<T> {
}
}

impl<K, V, H> FromVariant for HashMap<K, V, H>
where
K: FromVariant + Eq + Hash,
V: FromVariant,
H: BuildHasher + Default,
{
fn from_variant(variant: &Variant) -> Option<Self> {
let mut map = HashMap::default();

let n_children = variant.n_children();
if n_children != 0 {
for i in 0..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<K, V> ToVariant for HashMap<K, V>
where
K: StaticVariantType + ToVariant + Eq + Hash,
V: StaticVariantType + ToVariant,
{
fn to_variant(&self) -> Variant {
let mut vec = vec![];
for (key, value) in self {
let entry = Variant::new_dict_entry(
&key.to_variant(),
&value.to_variant(),
);
vec.push(entry);
}
Variant::new_array::<DictEntry<K, V>>(&vec)
}
}

struct DictEntry<K, V>(K, V);

impl<K: StaticVariantType, V: StaticVariantType> StaticVariantType for DictEntry<K, V> {
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<K: StaticVariantType, V: StaticVariantType, H: BuildHasher + Default> StaticVariantType for HashMap<K, V, H> {
fn static_variant_type() -> Cow<'static, VariantTy> {
let key_type = K::static_variant_type();
Expand Down

0 comments on commit b9d6948

Please sign in to comment.