use crate::{InitSans, Sans, step::Step};
pub struct MapInput<S, F> {
f: F,
coro: S,
}
pub fn map_input<S, F>(f: F, coro: S) -> MapInput<S, F> {
MapInput { f, coro }
}
pub fn init_map_input<I1, I2, O, S, F>(f: F, coro: S) -> MapInput<S, F>
where
S: InitSans<I2, O>,
F: FnMut(I1) -> I2,
{
MapInput { f, coro }
}
impl<I1, I2, O, S, F> Sans<I1, O> for MapInput<S, F>
where
S: Sans<I2, O>,
F: FnMut(I1) -> I2,
{
type Return = S::Return;
fn next(&mut self, input: I1) -> Step<O, Self::Return> {
let i2 = (self.f)(input);
self.coro.next(i2)
}
}
impl<I1, I2, O, S, F> InitSans<I1, O> for MapInput<S, F>
where
S: InitSans<I2, O>,
F: FnMut(I1) -> I2,
{
type Next = MapInput<S::Next, F>;
fn init(self) -> Step<(O, Self::Next), <S::Next as Sans<I2, O>>::Return> {
match self.coro.init() {
Step::Yielded((o, next)) => Step::Yielded((
o,
MapInput {
f: self.f,
coro: next,
},
)),
Step::Complete(d) => Step::Complete(d),
}
}
}
pub struct MapYield<S, F, I, O1> {
f: F,
coro: S,
_phantom: std::marker::PhantomData<(I, O1)>,
}
pub fn map_yield<I, O1, O2, S, F>(f: F, coro: S) -> MapYield<S, F, I, O1>
where
S: Sans<I, O1>,
F: FnMut(O1) -> O2,
{
MapYield {
f,
coro,
_phantom: std::marker::PhantomData,
}
}
pub fn init_map_yield<I, O1, O2, S, F>(f: F, coro: S) -> MapYield<S, F, I, O1>
where
S: InitSans<I, O1>,
F: FnMut(O1) -> O2,
{
MapYield {
f,
coro,
_phantom: std::marker::PhantomData,
}
}
impl<I, O1, O2, S, F> Sans<I, O2> for MapYield<S, F, I, O1>
where
S: Sans<I, O1>,
F: FnMut(O1) -> O2,
{
type Return = S::Return;
fn next(&mut self, input: I) -> Step<O2, Self::Return> {
match self.coro.next(input) {
Step::Yielded(o1) => Step::Yielded((self.f)(o1)),
Step::Complete(a) => Step::Complete(a),
}
}
}
impl<I, O1, O2, S, F> InitSans<I, O2> for MapYield<S, F, I, O1>
where
S: InitSans<I, O1>,
F: FnMut(O1) -> O2,
{
type Next = MapYield<S::Next, F, I, O1>;
fn init(self) -> Step<(O2, Self::Next), <S::Next as Sans<I, O1>>::Return> {
match self.coro.init() {
Step::Yielded((o1, next)) => {
let mut f = self.f;
let o2 = f(o1);
Step::Yielded((
o2,
MapYield {
f,
coro: next,
_phantom: std::marker::PhantomData,
},
))
}
Step::Complete(d) => Step::Complete(d),
}
}
}
pub struct MapReturn<S, F> {
f: F,
coro: S,
}
pub fn map_return<S, F>(f: F, coro: S) -> MapReturn<S, F> {
MapReturn { f, coro }
}
pub fn init_map_return<I, O, D1, D2, S, F>(f: F, coro: S) -> MapReturn<S, F>
where
S: InitSans<I, O>,
S::Next: Sans<I, O, Return = D1>,
F: FnMut(D1) -> D2,
{
MapReturn { f, coro }
}
impl<I, O, D1, D2, S, F> Sans<I, O> for MapReturn<S, F>
where
S: Sans<I, O, Return = D1>,
F: FnMut(D1) -> D2,
{
type Return = D2;
fn next(&mut self, input: I) -> Step<O, Self::Return> {
match self.coro.next(input) {
Step::Yielded(o) => Step::Yielded(o),
Step::Complete(r1) => Step::Complete((self.f)(r1)),
}
}
}
impl<I, O, D1, D2, S, F> InitSans<I, O> for MapReturn<S, F>
where
S: InitSans<I, O>,
S::Next: Sans<I, O, Return = D1>,
F: FnMut(D1) -> D2,
{
type Next = MapReturn<S::Next, F>;
fn init(self) -> Step<(O, Self::Next), D2> {
match self.coro.init() {
Step::Yielded((o, next)) => Step::Yielded((
o,
MapReturn {
f: self.f,
coro: next,
},
)),
Step::Complete(d1) => {
let mut f = self.f;
Step::Complete(f(d1))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::InitSans;
use crate::build::repeat;
#[test]
fn test_map_input_and_map_yield_pipeline() {
let mut total = 0_i64;
let init_coro = (
0_i64,
repeat(move |delta: i64| {
total += delta;
total
}),
)
.map_input(|cmd: &str| -> i64 {
let mut parts = cmd.split_whitespace();
let op = parts.next().expect("operation must exist");
let amount: i64 = parts
.next()
.expect("amount must exist")
.parse()
.expect("amount must parse");
match op {
"add" => amount,
"sub" => -amount,
_ => panic!("unsupported op: {op}"),
}
})
.map_yield(|value: i64| format!("total={value}"));
let (initial_total, mut coro) = init_coro.init().unwrap_yielded();
assert_eq!("total=0", initial_total);
assert_eq!("total=5", coro.next("add 5").unwrap_yielded());
assert_eq!("total=2", coro.next("sub 3").unwrap_yielded());
assert_eq!("total=7", coro.next("add 5").unwrap_yielded());
}
#[test]
fn test_map_input_basic() {
use crate::build::repeat;
let coro = repeat(|x: i32| x * 2);
let mut mapped = map_input(|s: &str| s.parse::<i32>().unwrap(), coro);
assert_eq!(mapped.next("5").unwrap_yielded(), 10);
assert_eq!(mapped.next("7").unwrap_yielded(), 14);
assert_eq!(mapped.next("10").unwrap_yielded(), 20);
}
#[test]
fn test_map_input_with_once() {
use crate::build::once;
let coro = once(|x: i32| x + 100);
let mut mapped = map_input(|s: String| s.len() as i32, coro);
assert_eq!(mapped.next("hello".to_string()).unwrap_yielded(), 105);
assert_eq!(mapped.next("world".to_string()).unwrap_complete(), 5);
}
#[test]
fn test_map_input_preserves_return() {
use crate::build::once;
let coro = once(|x: i32| x * 2);
let mut mapped = map_input(|x: i32| x + 1, coro);
mapped.next(5).unwrap_yielded();
assert_eq!(mapped.next(10).unwrap_complete(), 11);
}
#[test]
fn test_map_yield_basic() {
use crate::build::repeat;
let coro = repeat(|x: i32| x * 2);
let mut mapped = map_yield(|y: i32| y.to_string(), coro);
assert_eq!(mapped.next(5).unwrap_yielded(), "10");
assert_eq!(mapped.next(7).unwrap_yielded(), "14");
assert_eq!(mapped.next(100).unwrap_yielded(), "200");
}
#[test]
fn test_map_yield_with_once() {
use crate::build::once;
let coro = once(|x: i32| x + 10);
let mut mapped = map_yield(|y: i32| format!("result={}", y), coro);
assert_eq!(mapped.next(5).unwrap_yielded(), "result=15");
assert_eq!(mapped.next(20).unwrap_complete(), 20);
}
#[test]
fn test_map_yield_preserves_return() {
use crate::build::once;
let coro = once(|x: i32| x * 2);
let mut mapped = map_yield(|y: i32| y as f64, coro);
assert_eq!(mapped.next(5).unwrap_yielded(), 10.0);
assert_eq!(mapped.next(7).unwrap_complete(), 7);
}
#[test]
fn test_map_return_basic() {
use crate::build::once;
let coro = once(|x: i32| x + 5);
let mut mapped = map_return(|r: i32| r * 10, coro);
assert_eq!(mapped.next(10).unwrap_yielded(), 15);
assert_eq!(mapped.next(20).unwrap_complete(), 200);
}
#[test]
fn test_map_return_with_repeat() {
use crate::build::repeat;
let coro = repeat(|x: i32| x + 1);
let _mapped = map_return(|r: i32| r.to_string(), coro);
}
#[test]
fn test_map_return_yield_passthrough() {
use crate::build::once;
let coro = once(|x: i32| x * 2);
let mut mapped = map_return(|r: i32| format!("done:{}", r), coro);
assert_eq!(mapped.next(5).unwrap_yielded(), 10);
assert_eq!(mapped.next(7).unwrap_complete(), "done:7");
}
#[test]
fn test_map_return_type_conversion() {
use crate::build::once;
let coro = once(|x: i32| x + 1);
let mut mapped = map_return(|r: i32| (r as f64, r * 2), coro);
mapped.next(5).unwrap_yielded(); assert_eq!(mapped.next(10).unwrap_complete(), (10.0, 20));
}
#[test]
fn test_all_three_maps_combined() {
use crate::build::once;
let coro = once(|x: i32| x * 2);
let mut mapped = map_return(
|r: i32| r as f64,
map_yield(
|y: i32| format!("yielded:{}", y),
map_input(|s: &str| s.parse::<i32>().unwrap(), coro),
),
);
assert_eq!(mapped.next("5").unwrap_yielded(), "yielded:10");
assert_eq!(mapped.next("7").unwrap_complete(), 7.0);
}
#[test]
fn test_map_input_multiple_transformations() {
use crate::build::repeat;
let coro = repeat(|x: i32| x + 1);
let mut mapped = map_input(|s: String| s.len(), map_input(|n: usize| n as i32, coro));
assert_eq!(mapped.next("hello".to_string()).unwrap_yielded(), 6);
assert_eq!(mapped.next("a".to_string()).unwrap_yielded(), 2);
}
}