// Take a look at the license at the top of the repository in the LICENSE file. use glib::ffi::{gconstpointer, gpointer}; use glib::translate::*; use glib::value::{FromValue, ToValue}; use glib::StaticType; use glib::Value; use std::ffi::CString; use std::fmt; use std::iter; use std::marker::PhantomData; use std::mem; use std::ptr; use std::sync::Arc; use thiserror::Error; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Error)] pub enum IteratorError { #[error("Resync")] Resync, #[error("Error")] Error, } // Implemented manually so that we can use generics for the item pub struct Iterator { iter: ptr::NonNull, phantom: PhantomData, } impl Iterator where for<'a> T: FromValue<'a> + 'static, { pub unsafe fn into_ptr(self) -> *mut ffi::GstIterator { let s = mem::ManuallyDrop::new(self); let it = s.to_glib_none().0; it as *mut _ } #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Result, IteratorError> { unsafe { let mut value = Value::uninitialized(); let res = ffi::gst_iterator_next(self.to_glib_none_mut().0, value.to_glib_none_mut().0); #[allow(clippy::wildcard_in_or_patterns)] match res { ffi::GST_ITERATOR_OK => match value.get::() { Ok(value) => Ok(Some(value)), Err(_) => Err(IteratorError::Error), }, ffi::GST_ITERATOR_DONE => Ok(None), ffi::GST_ITERATOR_RESYNC => Err(IteratorError::Resync), ffi::GST_ITERATOR_ERROR | _ => Err(IteratorError::Error), } } } pub fn resync(&mut self) { unsafe { ffi::gst_iterator_resync(self.to_glib_none_mut().0); } } pub fn filter(self, func: F) -> Self where F: Fn(T) -> bool + Send + Sync + 'static, T: StaticType, { unsafe { let func_box: Box bool + Send + Sync + 'static> = Box::new(func); let mut closure_value = glib::Value::from_type(from_glib(filter_boxed_get_type::())); glib::gobject_ffi::g_value_take_boxed( closure_value.to_glib_none_mut().0, Arc::into_raw(Arc::new(func_box)) as gpointer, ); from_glib_full(ffi::gst_iterator_filter( self.into_ptr(), Some(filter_trampoline::), closure_value.to_glib_none().0, )) } } pub fn find(&mut self, func: F) -> Option where F: FnMut(T) -> bool, { unsafe { let mut elem = glib::Value::uninitialized(); let mut func = func; let func_ptr = &mut func as *mut F as gpointer; let res = from_glib(ffi::gst_iterator_find_custom( self.to_glib_none_mut().0, Some(find_trampoline::), elem.to_glib_none_mut().0, func_ptr, )); if res { Some(elem.get::().expect("Iterator::find")) } else { None } } } pub fn foreach(&mut self, func: F) -> Result<(), IteratorError> where F: FnMut(T), { unsafe { let mut func = func; let func_ptr = &mut func as *mut F as gpointer; let res = ffi::gst_iterator_foreach( self.to_glib_none_mut().0, Some(foreach_trampoline::), func_ptr, ); #[allow(clippy::wildcard_in_or_patterns)] match res { ffi::GST_ITERATOR_OK | ffi::GST_ITERATOR_DONE => Ok(()), ffi::GST_ITERATOR_RESYNC => Err(IteratorError::Resync), ffi::GST_ITERATOR_ERROR | _ => Err(IteratorError::Error), } } } pub fn fold(&mut self, init: U, func: F) -> Result where F: FnMut(U, T) -> Result, { unsafe { let mut func = func; let func_ptr = &mut func as *mut F as gpointer; let mut accum = Some(init); let mut ret = glib::Value::from_type(glib::Type::POINTER); glib::gobject_ffi::g_value_set_pointer( ret.to_glib_none_mut().0, &mut accum as *mut _ as gpointer, ); let res = ffi::gst_iterator_fold( self.to_glib_none_mut().0, Some(fold_trampoline::), ret.to_glib_none_mut().0, func_ptr, ); #[allow(clippy::wildcard_in_or_patterns)] match res { ffi::GST_ITERATOR_OK | ffi::GST_ITERATOR_DONE => Ok(accum.unwrap()), ffi::GST_ITERATOR_RESYNC => Err(IteratorError::Resync), ffi::GST_ITERATOR_ERROR | _ => Err(IteratorError::Error), } } } } impl Iterator where for<'a> T: FromValue<'a> + StaticType + ToValue + Send + 'static, { pub fn new>(imp: I) -> Self { assert_initialized_main_thread!(); static DUMMY_COOKIE: u32 = 0; unsafe { let it = ffi::gst_iterator_new( mem::size_of::>() as u32, T::static_type().to_glib(), ptr::null_mut(), &DUMMY_COOKIE as *const _ as *mut _, Some(rs_iterator_copy::), Some(rs_iterator_next::), None, Some(rs_iterator_resync::), Some(rs_iterator_free::), ); { let it = it as *mut RsIterator; ptr::write(&mut (*it).imp, imp); } from_glib_full(it) } } } impl Iterator where for<'a> T: FromValue<'a> + StaticType + ToValue + Clone + Send + 'static, { pub fn from_vec(items: Vec) -> Self { skip_assert_initialized!(); Self::new(VecIteratorImpl::new(items)) } } #[repr(C)] struct RsIterator> where for<'a> T: FromValue<'a> + StaticType + ToValue + Send + 'static, { iter: ffi::GstIterator, imp: I, phantom: PhantomData, } pub trait IteratorImpl: Clone + Send + 'static where for<'a> T: FromValue<'a> + StaticType + ToValue + Send + 'static, { fn next(&mut self) -> Option>; fn resync(&mut self); } unsafe extern "C" fn rs_iterator_copy>( it: *const ffi::GstIterator, copy: *mut ffi::GstIterator, ) where for<'a> T: FromValue<'a> + StaticType + ToValue + Send + 'static, { let it = it as *const RsIterator; let copy = copy as *mut RsIterator; ptr::write(&mut (*copy).imp, (*it).imp.clone()); } unsafe extern "C" fn rs_iterator_free>(it: *mut ffi::GstIterator) where for<'a> T: FromValue<'a> + StaticType + ToValue + Send + 'static, { let it = it as *mut RsIterator; ptr::drop_in_place(&mut (*it).imp); } unsafe extern "C" fn rs_iterator_next>( it: *mut ffi::GstIterator, result: *mut glib::gobject_ffi::GValue, ) -> ffi::GstIteratorResult where for<'a> T: FromValue<'a> + StaticType + ToValue + Send + 'static, { let it = it as *mut RsIterator; match (*it).imp.next() { Some(Ok(value)) => { let value = value.to_value(); ptr::write(result, value.into_raw()); ffi::GST_ITERATOR_OK } None => ffi::GST_ITERATOR_DONE, Some(Err(res)) => match res { IteratorError::Resync => ffi::GST_ITERATOR_RESYNC, IteratorError::Error => ffi::GST_ITERATOR_ERROR, }, } } unsafe extern "C" fn rs_iterator_resync>(it: *mut ffi::GstIterator) where for<'a> T: FromValue<'a> + StaticType + ToValue + Send + 'static, { let it = it as *mut RsIterator; (*it).imp.resync(); } #[derive(Clone)] struct VecIteratorImpl { pos: usize, items: Vec, } impl VecIteratorImpl where for<'a> T: StaticType + ToValue + FromValue<'a> + Clone + Send + 'static, { fn new(items: Vec) -> Self { skip_assert_initialized!(); Self { pos: 0, items } } } impl IteratorImpl for VecIteratorImpl where for<'a> T: StaticType + ToValue + FromValue<'a> + Clone + Send + 'static, { fn next(&mut self) -> Option> { if self.pos < self.items.len() { let res = Ok(self.items[self.pos].clone()); self.pos += 1; return Some(res); } None } fn resync(&mut self) { self.pos = 0; } } unsafe impl Send for Iterator {} unsafe impl Sync for Iterator {} unsafe extern "C" fn filter_trampoline(value: gconstpointer, func: gconstpointer) -> i32 where for<'a> T: FromValue<'a> + 'static, { let value = value as *const glib::gobject_ffi::GValue; let func = func as *const glib::gobject_ffi::GValue; let func = glib::gobject_ffi::g_value_get_boxed(func); let func = &*(func as *const &(dyn Fn(T) -> bool + Send + Sync + 'static)); let value = &*(value as *const glib::Value); let value = value.get::().expect("Iterator filter_trampoline"); if func(value) { 0 } else { -1 } } unsafe extern "C" fn filter_boxed_ref(boxed: gpointer) -> gpointer { let boxed = Arc::from_raw(boxed as *const Box bool + Send + Sync + 'static>); let copy = Arc::clone(&boxed); // Forget it and keep it alive, we will still need it later let _ = Arc::into_raw(boxed); Arc::into_raw(copy) as gpointer } unsafe extern "C" fn filter_boxed_unref(boxed: gpointer) { let _ = Arc::from_raw(boxed as *const Box bool + Send + Sync + 'static>); } unsafe extern "C" fn filter_boxed_get_type() -> glib::ffi::GType { use once_cell::sync::Lazy; use std::collections::HashMap; use std::sync::Mutex; static mut TYPES: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); let mut types = TYPES.lock().unwrap(); let type_name = T::static_type().name(); if let Some(type_) = types.get(type_name) { return *type_; } let type_ = { let iter_type_name = { let mut idx = 0; loop { let iter_type_name = CString::new(format!("GstRsIteratorFilterBoxed-{}-{}", type_name, idx)) .unwrap(); if glib::gobject_ffi::g_type_from_name(iter_type_name.as_ptr()) == glib::gobject_ffi::G_TYPE_INVALID { break iter_type_name; } idx += 1; } }; let type_ = glib::gobject_ffi::g_boxed_type_register_static( iter_type_name.as_ptr(), Some(filter_boxed_ref::), Some(filter_boxed_unref::), ); assert_ne!(type_, glib::gobject_ffi::G_TYPE_INVALID); type_ }; types.insert(String::from(type_name), type_); type_ } unsafe extern "C" fn find_trampoline bool>( value: gconstpointer, func: gconstpointer, ) -> i32 where for<'a> T: FromValue<'a> + 'static, { let value = value as *const glib::gobject_ffi::GValue; let func = func as *mut F; let value = &*(value as *const glib::Value); let value = value.get::().expect("Iterator find_trampoline"); if (*func)(value) { 0 } else { -1 } } unsafe extern "C" fn foreach_trampoline( value: *const glib::gobject_ffi::GValue, func: gpointer, ) where for<'a> T: FromValue<'a> + 'static, { let func = func as *mut F; let value = &*(value as *const glib::Value); let value = value.get::().expect("Iterator foreach_trampoline"); (*func)(value); } unsafe extern "C" fn fold_trampoline Result>( value: *const glib::gobject_ffi::GValue, ret: *mut glib::gobject_ffi::GValue, func: gpointer, ) -> glib::ffi::gboolean where for<'a> T: FromValue<'a> + 'static, { let func = func as *mut F; let value = &*(value as *const glib::Value); let value = value.get::().expect("Iterator fold_trampoline"); let accum = &mut *(glib::gobject_ffi::g_value_get_pointer(ret) as *mut Option); match (*func)(accum.take().unwrap(), value) { Ok(next_accum) => { *accum = Some(next_accum); glib::ffi::GTRUE } Err(next_accum) => { *accum = Some(next_accum); glib::ffi::GFALSE } } } impl Clone for Iterator { fn clone(&self) -> Self { unsafe { from_glib_full(ffi::gst_iterator_copy(self.to_glib_none().0)) } } } impl fmt::Debug for Iterator { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Iterator") .field("iter", &self.iter) .finish() } } impl Drop for Iterator { fn drop(&mut self) { unsafe { ffi::gst_iterator_free(self.iter.as_ptr()); } } } impl iter::IntoIterator for Iterator where for<'a> T: FromValue<'a> + 'static, { type Item = Result; type IntoIter = StdIterator; fn into_iter(self) -> Self::IntoIter { Self::IntoIter::new(self) } } impl glib::types::StaticType for Iterator { fn static_type() -> glib::types::Type { unsafe { glib::translate::from_glib(ffi::gst_iterator_get_type()) } } } impl glib::value::ValueType for Iterator { type Type = Self; } unsafe impl<'a, T: StaticType + 'static> glib::value::FromValue<'a> for Iterator { type Checker = glib::value::GenericValueTypeOrNoneChecker; unsafe fn from_value(value: &'a glib::Value) -> Self { skip_assert_initialized!(); from_glib_none( glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut ffi::GstIterator ) } } impl glib::value::ToValue for Iterator { fn to_value(&self) -> glib::Value { let mut value = glib::Value::for_value_type::(); unsafe { glib::gobject_ffi::g_value_set_boxed( value.to_glib_none_mut().0, self.to_glib_none().0 as *mut _, ) } value } fn value_type(&self) -> glib::Type { Self::static_type() } } impl glib::value::ToValueOptional for Iterator { fn to_value_optional(s: Option<&Self>) -> glib::Value { skip_assert_initialized!(); let mut value = glib::Value::for_value_type::(); unsafe { glib::gobject_ffi::g_value_set_boxed( value.to_glib_none_mut().0, s.to_glib_none().0 as *mut _, ) } value } } #[doc(hidden)] impl glib::translate::GlibPtrDefault for Iterator { type GlibType = *mut ffi::GstIterator; } #[doc(hidden)] impl<'a, T: 'static> glib::translate::ToGlibPtr<'a, *const ffi::GstIterator> for Iterator { type Storage = &'a Iterator; fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstIterator, Self> { glib::translate::Stash(self.iter.as_ptr(), self) } fn to_glib_full(&self) -> *const ffi::GstIterator { unimplemented!() } } #[doc(hidden)] impl<'a, T: 'static> glib::translate::ToGlibPtrMut<'a, *mut ffi::GstIterator> for Iterator { type Storage = &'a mut Iterator; #[inline] fn to_glib_none_mut( &'a mut self, ) -> glib::translate::StashMut<'a, *mut ffi::GstIterator, Self> { glib::translate::StashMut(self.iter.as_ptr(), self) } } #[doc(hidden)] impl glib::translate::FromGlibPtrNone<*const ffi::GstIterator> for Iterator { #[inline] unsafe fn from_glib_none(ptr: *const ffi::GstIterator) -> Self { assert_ne!( glib::gobject_ffi::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib::ffi::GFALSE ); from_glib_full(ffi::gst_iterator_copy(ptr)) } } #[doc(hidden)] impl glib::translate::FromGlibPtrNone<*mut ffi::GstIterator> for Iterator { #[inline] unsafe fn from_glib_none(ptr: *mut ffi::GstIterator) -> Self { assert_ne!( glib::gobject_ffi::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib::ffi::GFALSE ); from_glib_full(ffi::gst_iterator_copy(ptr)) } } #[doc(hidden)] impl glib::translate::FromGlibPtrBorrow<*mut ffi::GstIterator> for Iterator { #[inline] unsafe fn from_glib_borrow(ptr: *mut ffi::GstIterator) -> Borrowed { assert!(!ptr.is_null()); assert_ne!( glib::gobject_ffi::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib::ffi::GFALSE ); Borrowed::new(Self { iter: ptr::NonNull::new_unchecked(ptr), phantom: PhantomData, }) } } #[doc(hidden)] impl glib::translate::FromGlibPtrFull<*mut ffi::GstIterator> for Iterator { #[inline] unsafe fn from_glib_full(ptr: *mut ffi::GstIterator) -> Self { assert!(!ptr.is_null()); assert_ne!( glib::gobject_ffi::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib::ffi::GFALSE ); Self { iter: ptr::NonNull::new_unchecked(ptr), phantom: PhantomData, } } } pub struct StdIterator { inner: Iterator, error: Option, } impl StdIterator { fn new(inner: Iterator) -> Self { skip_assert_initialized!(); 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: FromValue<'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(), None => {} } let res = self.inner.next(); self.error = res.as_ref().err().copied(); res.transpose() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_vec() { crate::init().unwrap(); let vec = vec![1i32, 2, 3]; let mut it = Iterator::from_vec(vec); let val = it.next(); assert_eq!(val, Ok(Some(1))); let val = it.next(); assert_eq!(val, Ok(Some(2))); let val = it.next(); assert_eq!(val, Ok(Some(3))); assert_eq!(it.next(), Ok(None)); let vec = vec![1i32, 2, 3]; let mut it = Iterator::from_vec(vec); let mut vals = Vec::new(); while let Ok(Some(res)) = it.next() { vals.push(res); } assert_eq!(vals, [1, 2, 3]); } #[test] fn test_filter() { crate::init().unwrap(); let vec = vec![1i32, 2, 3]; let mut it = Iterator::from_vec(vec).filter(|val| val % 2 == 1); let mut vals = Vec::new(); while let Ok(Some(res)) = it.next() { vals.push(res); } assert_eq!(vals, [1, 3]); } #[test] fn test_find() { crate::init().unwrap(); // Our find let vec = vec![1i32, 2, 3]; let val = Iterator::from_vec(vec).find(|val| val == 2); assert_eq!(val.unwrap(), 2); } #[test] fn test_foreach() { crate::init().unwrap(); let vec = vec![1i32, 2, 3]; let mut sum = 0; let res = Iterator::from_vec(vec).foreach(|val| sum += val); assert_eq!(res, Ok(())); assert_eq!(sum, 6); } #[test] fn test_fold() { crate::init().unwrap(); // Our fold let vec = vec![1i32, 2, 3]; let res = Iterator::from_vec(vec).fold(0, |mut sum, val| { sum += val; Ok(sum) }); assert_eq!(res.unwrap(), 6); } #[test] fn test_std() { crate::init().unwrap(); let mut it = Iterator::from_vec(vec![1i32, 2, 3]).into_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_into_iter() { crate::init().unwrap(); let mut v = vec![1i32, 2, 3].into_iter(); for x in Iterator::from_vec(vec![1i32, 2, 3]) { assert_eq!(x.unwrap(), v.next().unwrap()); } assert_eq!(v.next(), None); } #[test] fn test_std_resync_collect() { use crate::prelude::*; use std::collections::BTreeSet; crate::init().unwrap(); let bin = crate::Bin::new(None); let id1 = crate::ElementFactory::make("identity", None).unwrap(); let id2 = crate::ElementFactory::make("identity", None).unwrap(); bin.add(&id1).unwrap(); let mut it = bin.iterate_elements().into_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 crate::prelude::*; crate::init().unwrap(); let bin = crate::Bin::new(None); let id1 = crate::ElementFactory::make("identity", None).unwrap(); let id2 = crate::ElementFactory::make("identity", None).unwrap(); bin.add(&id1).unwrap(); let mut it = bin.iterate_elements().into_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); } }