| use pyo3::prelude::*; |
| use std::collections::VecDeque; |
|
|
| |
| |
| |
| pub struct MaybeSizedIterator<I> { |
| length: Option<usize>, |
| iter: I, |
| } |
|
|
| impl<I> MaybeSizedIterator<I> |
| where |
| I: Iterator, |
| { |
| pub fn new(iter: I, length: Option<usize>) -> Self { |
| Self { length, iter } |
| } |
| } |
|
|
| impl<I> Iterator for MaybeSizedIterator<I> |
| where |
| I: Iterator, |
| { |
| type Item = I::Item; |
|
|
| fn next(&mut self) -> Option<Self::Item> { |
| self.iter.next() |
| } |
|
|
| fn size_hint(&self) -> (usize, Option<usize>) { |
| (self.length.unwrap_or(0), None) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| pub struct PyBufferedIterator<T, F> { |
| iter: Option<Py<PyAny>>, |
| converter: F, |
| buffer: VecDeque<PyResult<T>>, |
| size: usize, |
| } |
|
|
| impl<T, F, I> PyBufferedIterator<T, F> |
| where |
| F: Fn(Bound<'_, PyAny>) -> I, |
| I: IntoIterator<Item = PyResult<T>>, |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| pub fn new(iter: &Bound<'_, PyAny>, converter: F, buffer_size: usize) -> PyResult<Self> { |
| let py = iter.py(); |
| let iter: Py<PyAny> = unsafe { |
| Bound::from_borrowed_ptr_or_err(py, pyo3::ffi::PyObject_GetIter(iter.as_ptr()))? |
| .to_object(py) |
| }; |
|
|
| Ok(Self { |
| iter: Some(iter), |
| converter, |
| buffer: VecDeque::with_capacity(buffer_size), |
| size: buffer_size, |
| }) |
| } |
|
|
| |
| fn refill(&mut self) -> PyResult<()> { |
| if self.iter.is_none() { |
| return Ok(()); |
| } |
|
|
| Python::with_gil(|py| loop { |
| if self.buffer.len() >= self.size { |
| return Ok(()); |
| } |
|
|
| match unsafe { |
| Bound::from_owned_ptr_or_opt( |
| py, |
| pyo3::ffi::PyIter_Next(self.iter.as_ref().unwrap().bind(py).as_ptr()), |
| ) |
| } { |
| Some(obj) => self.buffer.extend((self.converter)(obj)), |
| None => { |
| if PyErr::occurred(py) { |
| return Err(PyErr::fetch(py)); |
| } else { |
| self.iter = None; |
| } |
| } |
| }; |
|
|
| if self.iter.is_none() { |
| return Ok(()); |
| } |
| }) |
| } |
| } |
|
|
| impl<T, F, I> Iterator for PyBufferedIterator<T, F> |
| where |
| F: Fn(Bound<'_, PyAny>) -> I, |
| I: IntoIterator<Item = PyResult<T>>, |
| { |
| type Item = PyResult<T>; |
|
|
| fn next(&mut self) -> Option<Self::Item> { |
| if !self.buffer.is_empty() { |
| self.buffer.pop_front() |
| } else if self.iter.is_some() { |
| if let Err(e) = self.refill() { |
| return Some(Err(e)); |
| } |
| self.next() |
| } else { |
| None |
| } |
| } |
| } |
|
|