fsm_input_driven/
fsm-input-driven.rs

1use std::io::Write;
2
3use bicoro::*;
4mod turnstile;
5use turnstile::create;
6
7// Our terminal just reads lines
8pub struct Input(String);
9
10// It can output to stdout, or stderr
11pub enum Output {
12    StdOut(String),
13    StdErr(String),
14    Flush,
15}
16
17/// Reads input until we get a valid turnstile input
18/// Here we can handle if the inputs aren't correct
19pub fn needs_input() -> Coroutine<'static, Input, Output, turnstile::Input> {
20    // this is a recursive function. Any easy way to express a loop
21    fn loop_(input: Input) -> Coroutine<'static, Input, Output, turnstile::Input> {
22        match input.0.trim() {
23            "push" => result(turnstile::Input::Push),
24            "coin" => result(turnstile::Input::Coin),
25            other => {
26                let error = format!("Turnstile: 'I don't understand {}' \r\n", other);
27                let prompt = "What do you try instead?".to_string();
28
29                send(Output::StdErr(error))
30                    .and_then(|()| send(Output::StdOut(prompt)))
31                    .and_then(|()| send(Output::Flush))
32                    .and_then(|()| receive())
33                    .and_then(loop_) // we loop if it's invalid, we need to get that input for the turnstile!
34            }
35        }
36    }
37    // kicks off the loop
38    let initial_prompt = format!("What do you do?: ");
39    send(Output::StdOut(initial_prompt))
40        .and_then(|()| send(Output::Flush))
41        .and_then(|()| receive())
42        .and_then(loop_)
43}
44
45/// Simply display the transition
46pub fn on_output(o: turnstile::Output) -> Coroutine<'static, Input, Output, ()> {
47    send(Output::StdOut(format!(
48        "Turnstile responds with: '{}'\r\n",
49        o.to_string()
50    )))
51}
52
53pub fn main() {
54    let turnstile = create();
55
56    // we can run a child routine inside this routine, with the provided functiosn to convert
57    // the inputs and outputs. Result remains the same.
58    let mut composed = send(Output::StdOut(
59        "You are stopped by a turnstile!\r\n".to_string(),
60    ))
61    .and_then(|()| send(Output::Flush))
62    .and_then(|()| subroutine(needs_input, on_output, turnstile));
63
64    // This is the main loop. Notice it's really only concerned
65    // with handling inputs and outputs of the termninal.
66    loop {
67        match run_step(composed) {
68            StepResult::Done(_) => unreachable!(), // just for this case, as its a non-exiting coroutine
69            StepResult::Yield { output, next } => {
70                match output {
71                    Output::StdOut(o) => print!("{}", o),
72                    Output::StdErr(e) => print!("{}", e),
73                    Output::Flush => std::io::stdout().flush().unwrap(),
74                }
75                composed = *next;
76            }
77            StepResult::Next(fun) => {
78                // Prompts only appear when needed here
79                let mut buf = String::new();
80                std::io::stdin().read_line(&mut buf).unwrap();
81                let input = Input(buf);
82                composed = fun(input);
83            }
84        }
85    }
86}