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
// In this example, we create a simple dispatcher with 3 possible event types:
//  1. `ping`. This command simply returns "pong".
//  2. `print`. This command prints the value stored in the program.
//  3. `set_value`. This command sets the value that is stored in the program.
//
// Usage:
//
// ```
// >> ping
// Pong
// >> print
// 0
// >> set_value 123
// 123 stored
// >> print
// 123
// ```

use std::{
    io::Write,
    ops::ControlFlow,
    sync::{
        atomic::{AtomicI32, Ordering},
        Arc,
    },
};

use dptree::prelude::*;

#[tokio::main]
async fn main() {
    let store = Arc::new(AtomicI32::new(0));

    let dispatcher = dptree::entry()
        .branch(ping_handler())
        .branch(set_value_handler())
        .branch(print_value_handler());

    repl(dispatcher, store).await
}

async fn repl(dispatcher: Handler<'static, DependencyMap, String>, store: Arc<AtomicI32>) -> ! {
    loop {
        print!(">> ");
        std::io::stdout().flush().unwrap();

        let mut cmd = String::new();
        std::io::stdin().read_line(&mut cmd).unwrap();

        let strs = cmd.trim().split(' ').collect::<Vec<_>>();
        let event = Event::parse(strs.as_slice());

        let out = match event {
            Some(event) => match dispatcher.dispatch(dptree::deps![event, store.clone()]).await {
                ControlFlow::Continue(event) => panic!("Unhandled event {:?}", event),
                ControlFlow::Break(result) => result,
            },
            _ => "Unknown command".to_string(),
        };

        println!("{}", out);
    }
}

#[derive(Copy, Clone, Debug)]
enum Event {
    Ping,
    SetValue(i32),
    PrintValue,
}

impl Event {
    fn parse(input: &[&str]) -> Option<Self> {
        match input {
            ["ping"] => Some(Event::Ping),
            ["set_value", value] => Some(Event::SetValue(value.parse().ok()?)),
            ["print"] => Some(Event::PrintValue),
            _ => None,
        }
    }
}

type CommandHandler = Endpoint<'static, DependencyMap, String>;

fn ping_handler() -> CommandHandler {
    dptree::filter_async(|event: Event| async move { matches!(event, Event::Ping) })
        .endpoint(|| async { "Pong".to_string() })
}

fn set_value_handler() -> CommandHandler {
    dptree::filter_map_async(|event: Event| async move {
        match event {
            Event::SetValue(value) => Some(value),
            _ => None,
        }
    })
    .endpoint(move |value: i32, store: Arc<AtomicI32>| async move {
        store.store(value, Ordering::SeqCst);
        format!("{} stored", value)
    })
}

fn print_value_handler() -> CommandHandler {
    dptree::filter_async(|event: Event| async move { matches!(event, Event::PrintValue) }).endpoint(
        move |store: Arc<AtomicI32>| async move {
            let value = store.load(Ordering::SeqCst);
            format!("{}", value)
        },
    )
}