use std::marker::PhantomData;
pub trait MapOk<T, E, F, U>: Sized
where
F: Fn(T) -> U,
{
type Iter: Iterator<Item = Result<U, E>>;
fn map_ok(self, f: F) -> Self::Iter;
}
impl<I, T, E, U, F> MapOk<T, E, F, U> for I
where
I: Iterator<Item = Result<T, E>>,
F: Fn(T) -> U,
{
type Iter = MapOkIter<Self, T, E, U, F>;
fn map_ok(self, f: F) -> Self::Iter {
MapOkIter {
iter: self,
f,
_phantom: PhantomData,
}
}
}
pub struct MapOkIter<I, T, E, U, F> {
iter: I,
f: F,
_phantom: PhantomData<MapFn<T, E, U>>,
}
type MapFn<T, E, U> = fn(T, E) -> (U, Result<T, E>);
impl<I, T, E, U, F> Iterator for MapOkIter<I, T, E, U, F>
where
I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> U,
{
type Item = Result<U, E>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Some(Ok(value)) => Some(Ok((self.f)(value))),
Some(Err(e)) => Some(Err(e)),
None => None,
}
}
}
#[cfg(test)]
mod tests {
use std::num::ParseIntError;
use std::str::FromStr;
use super::*;
struct Person {
age: u8,
}
impl Person {
fn new(age: u8) -> Self {
Person { age }
}
}
impl FromStr for Person {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let age = u8::from_str(s)?;
Ok(Person::new(age))
}
}
#[test]
fn map_ok_works() {
let input = ["10", "20", "x", "30"];
let mut iterator = input.into_iter().map(Person::from_str).map_ok(|p| p.age);
assert_eq!(iterator.next(), Some(Ok(10)));
assert_eq!(iterator.next(), Some(Ok(20)));
assert!(iterator.next().unwrap().is_err());
assert_eq!(iterator.next(), Some(Ok(30)));
assert_eq!(iterator.next(), None);
}
#[test]
fn naive() {
let input = ["10", "20", "x", "30"];
let mut iterator = input.into_iter().map(u32::from_str).map(|p| match p {
Ok(p) => Ok(p * 100),
Err(e) => Err(e),
});
assert_eq!(iterator.next(), Some(Ok(1000)));
assert_eq!(iterator.next(), Some(Ok(2000)));
assert!(iterator.next().unwrap().is_err());
assert_eq!(iterator.next(), Some(Ok(3000)));
assert_eq!(iterator.next(), None);
}
}