1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! This module contains functions that provide easier
//! to use workflows for the co-routine.
//! This can be implemented outside the crate, but are here for convenience.

use super::*;

/// Suspend this coroutine until an input arrives
///
/// The result can be bound. Eg. the below reads a int and converts it into a string
/// ```
/// use bicoro::*;
/// let co :Coroutine<i32,(),i32> = receive();
/// ```
pub fn receive<'a, I, O>() -> Coroutine<'a, I, O, I> {
    suspend(Box::new(result))
}

/// Map the inner type of the coroutine
///
/// This is sugar of bind and result
/// ```
/// use bicoro::*;
/// let co :Coroutine<i32,(),String> = map(receive(), |a| a.to_string());
/// ```
pub fn map<'a, I, O, A, B, F>(co: Coroutine<'a, I, O, A>, map: F) -> Coroutine<'a, I, O, B>
where
    F: FnOnce(A) -> B + 'a,
{
    bind(co, move |a| result(map(a)))
}

/// Runs a subroutine and converts it to the hosted type
///
/// As the child subroutine may have different inputs and outputs
/// you need to specify how to convert between these worlds.
/// This is done by providing functions that yield coroutines that can be
/// ran in the 'parent' context. One that is ran whenever the child needs an input
/// and one that is ran when the child produces an output
///
/// These can simply proxy through to the parents context, or
/// perform more processing. Both still retain inputs and outputs
/// so they may call a parents recieve multiple times
/// ```
/// use bicoro::*;
///
/// /* Types in this example are written explicitly to highlight what they are
///    It should be possible to use more compact forms
/// */
///
/// // Reads a value, echos it, then returns 0u8
/// let child: Coroutine<i64,i64,u8> = receive().and_then(send).and_then(|()| result(0u8));
///
/// // an example where we require two inputs from the parent context, to one on the child
/// // in this case we add the two i32's together to form an i64
/// let on_input = | | -> Coroutine<i32,_,i64> {
///     bind(receive(),|a| {
///         map(receive(),move |b| {
///             (a as i64+b as i64)
///         })
///     })
/// };
///
/// // parent expects strings on it's output channel so we convert
/// // note: type gaps used here as it must line up with the parent context types
/// // also means it's possible to read from the parent context when producing output
/// let on_output = | o:i64 | -> Coroutine<_,String,()> {
///     send(o.to_string())
/// };
///
/// // run_child maps the child routine into the parenst 'world'
/// // we can see the type of this is the parent coroutine
/// let parent : Coroutine<i32,String,u8> = run_child(on_input,on_output,child);
/// ```
pub fn run_child<'a, Input, Output, ChildInput, ChildOutput, OnInput, OnOutput, Result>(
    on_input: OnInput,
    on_output: OnOutput,
    child: Coroutine<'a, ChildInput, ChildOutput, Result>,
) -> Coroutine<'a, Input, Output, Result>
where
    OnInput: Fn() -> Coroutine<'a, Input, Output, ChildInput> + 'a,
    OnOutput: Fn(ChildOutput) -> Coroutine<'a, Input, Output, ()> + 'a,
{
    match run_step(child) {
        StepResult::Done(r) => result(r),
        StepResult::Yield { output, next } => {
            let output = on_output(output);
            bind(output, move |()| run_child(on_input, on_output, *next))
        }
        StepResult::Next(n) => {
            let rout = on_input();
            let cont = move |i| {
                let next = n(i);
                run_child(on_input, on_output, next)
            };
            bind(rout, cont)
        }
    }
}

/// Transforms the input of coroutine A into B
///
/// This requires a coroutine that can map B inputs
/// into a, as this is similar to running 'co'
/// in the context of the output
///
/// This is a specialization of run_child
///
/// TLDR; change Input with the inform transform function
pub fn transform_input<'a, Input, InputNested, Output, Transform, Result>(
    co: Coroutine<'a, InputNested, Output, Result>,
    transform: Transform,
) -> Coroutine<'a, Input, Output, Result>
where
    Transform: Fn(Input) -> Coroutine<'a, Input, Output, InputNested> + Clone + 'a,
{
    let on_input =
        move || -> Coroutine<Input, Output, InputNested> { bind(receive(), transform.clone()) };
    let on_output = |o: Output| send(o);
    run_child(on_input, on_output, co)
}

/// Transforms the output of coroutine A into B
///
/// This requires a coroutine that can map B outputs
/// into a, as this is similar to running 'co'
/// in the context of the output
///
/// This is a specialization of run_child
///
/// TLDR; change output with the output transform function
pub fn transform_output<'a, Input, OutputA, OutputB, Transform, Result>(
    co: Coroutine<'a, Input, OutputA, Result>,
    transform: Transform,
) -> Coroutine<'a, Input, OutputB, Result>
where
    Transform: Fn(OutputA) -> Coroutine<'a, Input, OutputB, OutputB> + 'a,
    Result: Send + Sync,
    OutputA: Send + Sync,
{
    let on_input = || receive();
    let on_output = move |o: OutputA| bind(transform(o), send);
    run_child(on_input, on_output, co)
}

/// Runs recieve until f returns some
///
/// This is ran inside it's own coroutine, so
/// you can call send inside it.
pub fn recieve_until<'a, Input, Output, Result, F>(f: F) -> Coroutine<'a, Input, Output, Result>
where
    F: Fn(Input) -> Coroutine<'a, Input, Output, Option<Result>> + Clone + 'a,
{
    let input = bind(receive(), f.clone());
    bind(input, |opt| match opt {
        Some(v) => result(v),
        None => recieve_until(f),
    })
}

/// Runs two coroutines sequentially
///
/// This will run first until it completes, then second afterwards
/// until it completes
/// Returns both return results tupled together
pub fn tuple<'a, I, O, R1, R2>(
    first: Coroutine<'a, I, O, R1>,
    second: Coroutine<'a, I, O, R2>,
) -> Coroutine<'a, I, O, (R1, R2)> {
    bind(first, move |a| map(second, move |b| (a, b)))
}

/// Runs a routine before the second routine
///
/// Result of the first routine is ignored, second is returned
/// if you need both results, use tuple
pub fn right<'a, I, O, A, B>(
    left: Coroutine<'a, I, O, A>,
    right: Coroutine<'a, I, O, B>,
) -> Coroutine<'a, I, O, B> {
    map(tuple(left, right), |(_, b)| b)
}

/// Runs a routine before the second routine
///
/// Result of the first routine is returned, second is ignored
/// if you need both results, use tuple
pub fn left<'a, I, O, A, B>(
    left: Coroutine<'a, I, O, A>,
    right: Coroutine<'a, I, O, B>,
) -> Coroutine<'a, I, O, A> {
    map(tuple(left, right), |(a, _)| a)
}

/// Converts the return result to the unit type
///
/// Useful when you don't care what the coroutine occurs
/// mainly after its effects
pub fn void<'a, I, O, A>(co: Coroutine<'a, I, O, A>) -> Coroutine<'a, I, O, ()> {
    map(co, |_| ())
}