maybe-borrow 0.1.2

Macros for conditionally returning borrowed data.
Documentation
use maybe_borrow::prelude::*;

trait LendingIteratorBase<'iter, _Bound = &'iter Self>: 'iter {
    type Item: 'iter;
    fn next_base(&'iter mut self) -> Option<Self::Item>;
}

trait LendingIteratorLt<'iter>: 'iter {
    type ItemLt: 'iter;
    fn next_lt(&'iter mut self) -> Option<Self::ItemLt>;
}

impl<'iter, I: ?Sized + LendingIteratorBase<'iter>> LendingIteratorLt<'iter> for I {
    type ItemLt = I::Item;
    fn next_lt(&'iter mut self) -> Option<Self::ItemLt> {
        I::next_base(self)
    }
}

trait LendingIterator: for<'iter> LendingIteratorLt<'iter, ItemLt = Self::Item<'iter>> {
    type Item<'iter>
    where
        Self: 'iter;

    fn next(&mut self) -> Option<Self::Item<'_>> {
        self.next_lt()
    }
}

impl<I: ?Sized + for<'iter> LendingIteratorLt<'iter>> LendingIterator for I {
    type Item<'iter> = <Self as LendingIteratorLt<'iter>>::ItemLt;
}

enum WindowsState<T> {
    Created { size: usize },
    Initialized { data: Box<[T]> },
}

struct Windows<T, I> {
    state: WindowsState<T>,
    iter: I,
}

impl<T, I> Windows<T, I> {
    pub fn new(iter: impl IntoIterator<IntoIter = I>, size: usize) -> Self {
        let iter = iter.into_iter();
        Self {
            state: WindowsState::Created { size },
            iter,
        }
    }
}

impl<'iter, T, I> LendingIteratorBase<'iter> for Windows<T, I>
where
    I: Iterator<Item = T>,
{
    type Item = &'iter mut [T];
    fn next_base(&'iter mut self) -> Option<&'iter mut [T]> {
        match self.state {
            WindowsState::Created { size } => {
                let data: Box<[T]> = self.iter.by_ref().take(size).collect();
                if data.len() < size {
                    return None;
                }

                self.state = WindowsState::Initialized { data };
            }
            WindowsState::Initialized { ref data } if data.len() == 0 => {
                return Some(&mut []);
            }
            WindowsState::Initialized { ref mut data } => {
                let Some(item) = self.iter.next() else {
                    let size = data.len();
                    self.state = WindowsState::Created { size };
                    return None;
                };
                data.rotate_left(1);
                *data.last_mut().unwrap() = item;
            }
        }

        let WindowsState::Initialized { ref mut data } = self.state else {
            unreachable!();
        };

        Some(data)
    }
}

fn next_filtered<'iter, I: LendingIterator>(
    mut iter: &'iter mut I,
    mut predicate: impl FnMut(&I::Item<'_>) -> bool,
) -> Option<I::Item<'iter>> {
    loop {
        maybe_borrow!(for<'x> |iter| -> Option<I::Item<'x>> {
            match iter.next() {
                Some(ref item) if !predicate(item) => {}
                out => return_borrowed!(out),
            }
        });
    }
}

fn next_filtered_with_try<'iter, I: LendingIterator>(
    mut iter: &'iter mut I,
    mut predicate: impl FnMut(&I::Item<'_>) -> bool,
) -> Option<I::Item<'iter>> {
    loop {
        try_maybe_borrow!(for<'x> |iter| -> Option<I::Item<'x>> {
            let item = iter.next()?;
            if predicate(&item) {
                return_borrowed!(Some(item));
            }
        });
    }
}

#[test]
fn test_next_filtered() {
    let mut iter = Windows::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3);

    let items = Vec::from_iter(std::iter::from_fn(move || {
        next_filtered(&mut iter, |x| x[0] % 2 != 0).map(|x| x.to_vec())
    }));

    assert_eq!(items, [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9],])
}

#[test]
fn test_next_filtered_with_try() {
    let mut iter = Windows::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3);

    let items = Vec::from_iter(std::iter::from_fn(move || {
        next_filtered_with_try(&mut iter, |x| x[0] % 2 != 0)
            .map(|x| x.to_vec())
    }));

    assert_eq!(items, [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9],])
}