From a30935ad1f49ed41a0741643d41deb96f48fab12 Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Wed, 4 Sep 2019 14:34:05 +0200 Subject: [PATCH] Iterator: Add a wrapper implementing std's Iterator Transposing the item type lets us be a std-compatible Iterator. The iterator is automatically resynced when resuming iteration after yielding Resync. This lets some combinators like `collect` and `find` work properly. --- gstreamer/src/iterator.rs | 126 ++++++++++++++++++++++++++++++++++++++ gstreamer/src/lib.rs | 2 +- 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/gstreamer/src/iterator.rs b/gstreamer/src/iterator.rs index 2bdb750b1..632f5491c 100644 --- a/gstreamer/src/iterator.rs +++ b/gstreamer/src/iterator.rs @@ -18,6 +18,7 @@ use gst_sys; use std::error::Error; use std::ffi::CString; use std::fmt; +use std::iter; use std::marker::PhantomData; use std::mem; use std::ptr; @@ -180,6 +181,10 @@ where } } } + + pub fn iter(self) -> StdIterator { + StdIterator::new(self) + } } impl Iterator @@ -611,6 +616,65 @@ impl glib::translate::FromGlibPtrFull<*mut gst_sys::GstIterator> } } +pub struct StdIterator { + inner: Iterator, + error: Option, +} + +impl StdIterator { + fn new(inner: Iterator) -> Self { + Self { inner, error: None } + } +} + +impl Clone for StdIterator { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + error: self.error, + } + } +} + +impl fmt::Debug for StdIterator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("StdIterator") + .field("inner", &self.inner) + .field("error", &self.error) + .finish() + } +} + +impl iter::Iterator for StdIterator +where + for<'a> T: FromValueOptional<'a> + 'static, +{ + type Item = Result; + + fn next(&mut self) -> Option { + match self.error { + // Fuse the iterator after returning IteratorError::Error + Some(IteratorError::Error) => return None, + + // The iterator needs a resync + Some(IteratorError::Resync) => { + self.inner.resync(); + self.error = None; + } + + None => {} + } + + let res = self.inner.next(); + + if let Err(err) = &res { + self.error = Some(*err); + } + + res.transpose() + } +} + #[cfg(test)] mod tests { use super::*; @@ -685,4 +749,66 @@ mod tests { }); assert_eq!(res.unwrap(), 6); } + + #[test] + fn test_std() { + let mut it = Iterator::from_vec(vec![1i32, 2, 3]).iter(); + assert_eq!(it.next(), Some(Ok(1))); + assert_eq!(it.next(), Some(Ok(2))); + assert_eq!(it.next(), Some(Ok(3))); + assert_eq!(it.next(), None); + } + + #[test] + fn test_std_resync_collect() { + use prelude::*; + use std::collections::BTreeSet; + + ::init().unwrap(); + + let bin = ::Bin::new(None); + let id1 = ::ElementFactory::make("identity", None).unwrap(); + let id2 = ::ElementFactory::make("identity", None).unwrap(); + + bin.add(&id1).unwrap(); + + let mut it = bin.iterate_elements().iter(); + assert_eq!(it.next().unwrap().unwrap(), id1); + + bin.add(&id2).unwrap(); + + let res = it.by_ref().collect::, _>>().unwrap_err(); + assert_eq!(res, IteratorError::Resync); + + let mut elems = BTreeSet::new(); + elems.insert(id1); + elems.insert(id2); + + let res = it.by_ref().collect::, _>>().unwrap(); + assert_eq!(res, elems); + + let res = it.collect::, _>>().unwrap(); + assert!(res.is_empty()); + } + + #[test] + fn test_std_resync_find() { + use prelude::*; + + ::init().unwrap(); + + let bin = ::Bin::new(None); + let id1 = ::ElementFactory::make("identity", None).unwrap(); + let id2 = ::ElementFactory::make("identity", None).unwrap(); + + bin.add(&id1).unwrap(); + + let mut it = bin.iterate_elements().iter(); + assert_eq!(it.next().unwrap().unwrap(), id1); + + bin.add(&id2).unwrap(); + + let res = it.find(|x| x.as_ref() == Ok(&id1)); + assert_eq!(res.unwrap().unwrap(), id1); + } } diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index aaa109645..62648329f 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -221,7 +221,7 @@ cfg_if! { } } -pub use self::iterator::{Iterator, IteratorError, IteratorImpl}; +pub use self::iterator::{Iterator, IteratorError, IteratorImpl, StdIterator}; #[cfg(any(feature = "futures", feature = "dox"))] pub use bus::BusStream; pub use child_proxy::ChildProxyExtManual;