// Take a look at the license at the top of the repository in the LICENSE file. // 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, FusedIterator}; use crate::{translate::*, Variant}; // rustdoc-stripper-ignore-next /// Iterator over items in a variant. #[derive(Debug)] pub struct VariantIter { variant: Variant, 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 { if self.head == self.tail { None } else { let value = self.variant.child_value(self.head); self.head += 1; Some(value) } } fn size_hint(&self) -> (usize, Option) { let size = self.tail - self.head; (size, Some(size)) } fn count(self) -> usize { self.tail - self.head } fn nth(&mut self, n: usize) -> Option { let (end, overflow) = self.head.overflowing_add(n); if end >= self.tail || overflow { self.head = self.tail; None } else { self.head = end + 1; Some(self.variant.child_value(end)) } } fn last(self) -> Option { if self.head == self.tail { None } else { Some(self.variant.child_value(self.tail - 1)) } } } impl DoubleEndedIterator for VariantIter { fn next_back(&mut self) -> Option { if self.head == self.tail { None } else { self.tail -= 1; Some(self.variant.child_value(self.tail)) } } fn nth_back(&mut self, n: usize) -> Option { let (end, overflow) = self.tail.overflowing_sub(n); if end <= self.head || overflow { self.head = self.tail; None } else { self.tail = end - 1; Some(self.variant.child_value(end - 1)) } } } impl ExactSizeIterator for VariantIter {} impl FusedIterator for VariantIter {} // rustdoc-stripper-ignore-next /// Iterator over items in a variant of type `as`. #[derive(Debug)] pub struct VariantStrIter<'a> { variant: &'a Variant, head: usize, tail: usize, } impl<'a> VariantStrIter<'a> { pub(crate) fn new(variant: &'a Variant) -> Self { let tail = variant.n_children(); Self { variant, head: 0, tail, } } fn impl_get(&self, i: usize) -> &'a str { unsafe { let mut p: *mut libc::c_char = std::ptr::null_mut(); let s = b"&s\0"; ffi::g_variant_get_child( self.variant.to_glib_none().0, i, s as *const u8 as *const _, &mut p, std::ptr::null::(), ); let p = std::ffi::CStr::from_ptr(p); p.to_str().unwrap() } } } impl<'a> Iterator for VariantStrIter<'a> { type Item = &'a str; fn next(&mut self) -> Option<&'a str> { if self.head == self.tail { None } else { let v = self.impl_get(self.head); self.head += 1; Some(v) } } fn size_hint(&self) -> (usize, Option) { let size = self.tail - self.head; (size, Some(size)) } fn count(self) -> usize { self.tail - self.head } fn nth(&mut self, n: usize) -> Option<&'a str> { let (end, overflow) = self.head.overflowing_add(n); if end >= self.tail || overflow { self.head = self.tail; None } else { self.head = end + 1; Some(self.impl_get(end)) } } fn last(self) -> Option<&'a str> { if self.head == self.tail { None } else { Some(self.impl_get(self.tail - 1)) } } } impl<'a> DoubleEndedIterator for VariantStrIter<'a> { fn next_back(&mut self) -> Option<&'a str> { if self.head == self.tail { None } else { self.tail -= 1; Some(self.impl_get(self.tail)) } } fn nth_back(&mut self, n: usize) -> Option<&'a str> { let (end, overflow) = self.tail.overflowing_sub(n); if end <= self.head || overflow { self.head = self.tail; None } else { self.tail = end - 1; Some(self.impl_get(end - 1)) } } } impl<'a> ExactSizeIterator for VariantStrIter<'a> {} impl<'a> FusedIterator for VariantStrIter<'a> {} #[cfg(test)] mod tests { use std::collections::HashMap; use crate::{ prelude::*, variant::{DictEntry, Variant}, }; #[test] fn test_variant_iter_variant() { let v = Variant::from_variant(&"foo".to_string().to_variant()); let vec: Vec = 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_from_iter::([ "foo".to_string().to_variant(), "bar".to_string().to_variant(), ]); let vec: Vec = v.iter().map(|i| i.get().unwrap()).collect(); let a = vec!["foo".to_string(), "bar".to_string()]; assert_eq!(&vec, &a); let vec: Vec<_> = v.array_iter_str().unwrap().collect(); assert_eq!(&vec, &a); } #[test] fn test_variant_iter_tuple() { let v = Variant::tuple_from_iter([ "foo".to_string().to_variant(), "bar".to_string().to_variant(), ]); let vec: Vec = 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::>()); 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); } #[test] fn test_variant_iter_nth() { let v = Variant::array_from_iter::([ "0".to_string().to_variant(), "1".to_string().to_variant(), "2".to_string().to_variant(), "3".to_string().to_variant(), "4".to_string().to_variant(), "5".to_string().to_variant(), ]); let mut iter = v.iter(); assert_eq!(iter.len(), 6); assert_eq!( iter.nth(1).map(|v| v.get::().unwrap()), Some("1".into()) ); assert_eq!(iter.len(), 4); assert_eq!( iter.next().map(|v| v.get::().unwrap()), Some("2".into()) ); assert_eq!( iter.nth_back(2).map(|v| v.get::().unwrap()), Some("3".into()) ); assert_eq!(iter.len(), 0); assert_eq!(iter.next(), None); assert_eq!(iter.next_back(), None); } #[test] fn test_variant_iter_count() { let v = Variant::array_from_iter::([ "0".to_string().to_variant(), "1".to_string().to_variant(), "2".to_string().to_variant(), ]); let iter = v.iter(); assert_eq!(iter.len(), 3); assert_eq!(iter.count(), 3); } #[test] fn test_variant_iter_last() { let v = Variant::array_from_iter::([ "0".to_string().to_variant(), "1".to_string().to_variant(), "2".to_string().to_variant(), ]); let iter = v.iter(); assert_eq!(iter.len(), 3); assert_eq!( iter.last().map(|v| v.get::().unwrap()), Some("2".into()) ); } #[test] fn test_variant_str_iter_nth() { let v = Variant::array_from_iter::([ "0".to_string().to_variant(), "1".to_string().to_variant(), "2".to_string().to_variant(), "3".to_string().to_variant(), "4".to_string().to_variant(), "5".to_string().to_variant(), ]); let mut iter = v.array_iter_str().unwrap(); assert_eq!(iter.len(), 6); assert_eq!(iter.nth(1), Some("1")); assert_eq!(iter.len(), 4); assert_eq!(iter.next(), Some("2")); assert_eq!(iter.nth_back(2), Some("3")); assert_eq!(iter.len(), 0); assert_eq!(iter.next(), None); assert_eq!(iter.next_back(), None); } #[test] fn test_variant_str_iter_count() { let v = Variant::array_from_iter::([ "0".to_string().to_variant(), "1".to_string().to_variant(), "2".to_string().to_variant(), ]); let iter = v.array_iter_str().unwrap(); assert_eq!(iter.len(), 3); assert_eq!(iter.count(), 3); } #[test] fn test_variant_str_iter_last() { let v = Variant::array_from_iter::([ "0".to_string().to_variant(), "1".to_string().to_variant(), "2".to_string().to_variant(), ]); let iter = v.array_iter_str().unwrap(); assert_eq!(iter.len(), 3); assert_eq!(iter.last(), Some("2")); } }