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

Add a VariantIter type, and Variant::iter() method #683

Merged
merged 1 commit into from
Sep 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ pub use types::{StaticType, Type};
pub use value::{SendValue, ToSendValue, ToValue, TypedValue, Value};
pub use variant::{FromVariant, StaticVariantType, ToVariant, Variant};
pub use variant_dict::VariantDict;
pub use variant_iter::VariantIter;
pub use variant_type::{VariantTy, VariantType};

#[macro_use]
Expand Down Expand Up @@ -174,6 +175,7 @@ mod main_context_channel;
pub mod value;
pub mod variant;
mod variant_dict;
mod variant_iter;
mod variant_type;
pub use main_context_channel::{Receiver, Sender, SyncSender};
mod date;
Expand Down
15 changes: 15 additions & 0 deletions src/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ use value;
use StaticType;
use Type;
use Value;
use VariantIter;
use VariantTy;
use VariantType;

Expand Down Expand Up @@ -329,6 +330,20 @@ impl Variant {

unsafe { glib_sys::g_variant_n_children(self.to_glib_none().0) }
}

/// Create an iterator over items in the variant.
pub fn iter(&self) -> VariantIter {
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")
);

VariantIter::new(self.clone())
}
}

unsafe impl Send for Variant {}
Expand Down
113 changes: 113 additions & 0 deletions src/variant_iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2020, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

// This is similar to the GVariantIter provided by glib, but that would
// introduce a heap allocation and doesn't provide a way to determine how
// many items are left in the iterator.

use std::iter::{DoubleEndedIterator, ExactSizeIterator, Iterator};

use variant::Variant;

ids1024 marked this conversation as resolved.
Show resolved Hide resolved
/// Iterator over items in a variant.
#[derive(Debug)]
pub struct VariantIter {
ids1024 marked this conversation as resolved.
Show resolved Hide resolved
variant: Variant,
sdroege marked this conversation as resolved.
Show resolved Hide resolved
head: usize,
tail: usize,
}

impl VariantIter {
pub(crate) fn new(variant: Variant) -> Self {
let tail = variant.n_children();
Self {
variant,
head: 0,
tail,
}
}
}

impl Iterator for VariantIter {
type Item = Variant;

fn next(&mut self) -> Option<Variant> {
if self.head == self.tail {
None
} else {
let value = self.variant.get_child_value(self.head);
self.head += 1;
Some(value)
}
}
ids1024 marked this conversation as resolved.
Show resolved Hide resolved

fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.tail - self.head;
(size, Some(size))
}
}

impl DoubleEndedIterator for VariantIter {
fn next_back(&mut self) -> Option<Variant> {
if self.head == self.tail {
None
} else {
self.tail -= 1;
Some(self.variant.get_child_value(self.tail))
}
}
}

impl ExactSizeIterator for VariantIter {}

#[cfg(test)]
mod tests {
use prelude::*;
use std::collections::HashMap;
use variant::DictEntry;
use variant::Variant;

#[test]
fn test_variant_iter_variant() {
let v = Variant::variant(&"foo".to_string().to_variant());
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
assert_eq!(vec, vec!["foo".to_string()]);
}

#[test]
fn test_variant_iter_array() {
let v = Variant::array::<String>(&vec![
"foo".to_string().to_variant(),
"bar".to_string().to_variant(),
]);
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
assert_eq!(vec, vec!["foo".to_string(), "bar".to_string()]);
}

#[test]
fn test_variant_iter_tuple() {
let v = Variant::tuple(&vec![
"foo".to_string().to_variant(),
"bar".to_string().to_variant(),
]);
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
assert_eq!(vec, vec!["foo".to_string(), "bar".to_string()]);
}

#[test]
fn test_variant_iter_dictentry() {
let v = DictEntry::new("foo", 1337).to_variant();
println!("{:?}", v.iter().collect::<Vec<_>>());
assert_eq!(v.iter().count(), 2);
}

#[test]
fn test_variant_iter_map() {
let mut map = HashMap::new();
map.insert("foo", 1);
map.insert("bar", 1);
let v = map.to_variant();
assert_eq!(v.iter().count(), 2);
}
}