pub struct Handler<'a, Input, Output, Descr = Unspecified> { /* private fields */ }
Expand description

An instance that receives an input and decides whether to break a chain or pass the value further.

In order to create this structure, you can use the predefined functions from crate.

The difference between chaining and branching

Handlers can be chained via Handler::chain and branched via Handler::branch. To understand the difference between the two, consider the following examples: a.chain(b).c and a.branch(b).c.

In a.chain(b).c, the handler a is given the rest of the handler chain, b and c; if a decides to pass the value further, it invokes b. Then, if b decides to pass the value further, it invokes c. Thus, the handler chain is linear.

In a.branch(b).c, if a decides to pass the value further, it invokes b. But since b is “branched”, it receives an empty chain, so it cannot invoke c. Instead, if b decides to continue execution (ControlFlow::Continue), a invokes c; otherwise (ControlFlow::Break), the process is terminated. The handler chain is nested.

To sum up, chaining looks like this:

a -> b -> c

And branching looks like this:

a -> b
  -> c

This is very crucial when b is a filter: if it is chained, it decides whether or not to call c, but when it is branched, whether c is called depends solely on a.

Implementations

Chain two handlers to form a chain of responsibility.

Chaining is different from branching. See “The difference between chaining and branching”.

Examples
use dptree::prelude::*;

let handler: Handler<_, _> =
    dptree::filter(|x: i32| x > 0).chain(dptree::endpoint(|| async { "done" }));

assert_eq!(handler.dispatch(dptree::deps![10]).await, ControlFlow::Break("done"));
assert_eq!(
    handler.dispatch(dptree::deps![-10]).await,
    ControlFlow::Continue(dptree::deps![-10])
);
Examples found in repository?
examples/descr_locations.rs (line 102)
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
fn main() {
    #[rustfmt::skip]
    let some_tree: Handler<DependencyMap, _, _> = dptree::entry()
        .branch(
            dptree::filter(|| true)
                .endpoint(|| async {})
        )
        .branch(
            dptree::filter_async(|| async { true })
                .chain(dptree::filter_map(|| Some(1) ))
                .endpoint(|| async {})
        );

    get_locations(&some_tree).iter().for_each(|(name, loc)| println!("{name: <12} @ {loc}"));
}

Chain two handlers to make a tree of responsibility.

Chaining is different from branching. See “The difference between chaining and branching”.

Examples
use dptree::prelude::*;


#[derive(Debug, PartialEq)]
enum Output {
    Five,
    One,
    GT,
}

let dispatcher: Handler<_, _> = dptree::entry()
    .branch(dptree::filter(|num: i32| num == 5).endpoint(|| async move { Output::Five }))
    .branch(dptree::filter(|num: i32| num == 1).endpoint(|| async move { Output::One }))
    .branch(dptree::filter(|num: i32| num > 2).endpoint(|| async move { Output::GT }));

assert_eq!(dispatcher.dispatch(dptree::deps![5]).await, ControlFlow::Break(Output::Five));
assert_eq!(dispatcher.dispatch(dptree::deps![1]).await, ControlFlow::Break(Output::One));
assert_eq!(dispatcher.dispatch(dptree::deps![3]).await, ControlFlow::Break(Output::GT));
assert_eq!(
    dispatcher.dispatch(dptree::deps![0]).await,
    ControlFlow::Continue(dptree::deps![0])
);
Examples found in repository?
examples/simple_dispatcher.rs (line 35)
31
32
33
34
35
36
37
38
39
40
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
}
More examples
Hide additional examples
examples/state_machine.rs (line 16)
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
async fn main() {
    let state = CommandState::Inactive;

    let dispatcher = dptree::entry()
        .branch(active_handler())
        .branch(paused_handler())
        .branch(inactive_handler())
        .branch(exit_handler());

    repl(state, dispatcher).await
}

async fn repl(mut state: CommandState, dispatcher: Handler<'static, Store, CommandState>) {
    loop {
        println!("|| Current state is {}", state);
        print!(">> ");
        std::io::stdout().flush().unwrap();

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

        let str = cmd.trim();
        let event = Event::parse(str);

        let new_state = match event {
            Some(event) => match dispatcher.dispatch(dptree::deps![event, state.clone()]).await {
                ControlFlow::Break(new_state) => new_state,
                ControlFlow::Continue(_) => {
                    println!("There is no transition for the event");
                    continue;
                }
            },
            _ => {
                println!("Unknown event");
                continue;
            }
        };

        if new_state == CommandState::Exit {
            return;
        }

        state = new_state;
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CommandState {
    Active,
    Paused,
    Inactive,
    Exit,
}

impl Display for CommandState {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            CommandState::Active => f.write_str("Active"),
            CommandState::Paused => f.write_str("Paused"),
            CommandState::Inactive => f.write_str("Inactive"),
            CommandState::Exit => f.write_str("Exit"),
        }
    }
}

#[derive(Debug, Clone)]
pub enum Event {
    Begin,
    Pause,
    Resume,
    End,
    Exit,
}

impl Event {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "begin" => Some(Event::Begin),
            "pause" => Some(Event::Pause),
            "resume" => Some(Event::Resume),
            "end" => Some(Event::End),
            "exit" => Some(Event::Exit),
            _ => None,
        }
    }
}

type Store = dptree::di::DependencyMap;
type Transition = Endpoint<'static, Store, TransitionOut>;
type TransitionOut = CommandState;

mod transitions {
    use super::*;

    pub fn begin() -> Transition {
        dptree::case![Event::Begin].endpoint(|| async { CommandState::Active })
    }

    pub fn pause() -> Transition {
        dptree::case![Event::Pause].endpoint(|| async { CommandState::Paused })
    }

    pub fn end() -> Transition {
        dptree::case![Event::End].endpoint(|| async { CommandState::Inactive })
    }

    pub fn resume() -> Transition {
        dptree::case![Event::Resume].endpoint(|| async { CommandState::Active })
    }

    pub fn exit() -> Transition {
        dptree::case![Event::Exit].endpoint(|| async { CommandState::Exit })
    }
}

type FsmHandler = Handler<'static, Store, TransitionOut>;

fn active_handler() -> FsmHandler {
    dptree::case![CommandState::Active].branch(transitions::pause()).branch(transitions::end())
}

fn paused_handler() -> FsmHandler {
    dptree::case![CommandState::Paused].branch(transitions::resume()).branch(transitions::end())
}

fn inactive_handler() -> FsmHandler {
    dptree::case![CommandState::Inactive].branch(transitions::begin()).branch(transitions::exit())
}

fn exit_handler() -> FsmHandler {
    dptree::case![CommandState::Exit].branch(transitions::exit())
}
examples/descr_locations.rs (lines 96-99)
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
fn main() {
    #[rustfmt::skip]
    let some_tree: Handler<DependencyMap, _, _> = dptree::entry()
        .branch(
            dptree::filter(|| true)
                .endpoint(|| async {})
        )
        .branch(
            dptree::filter_async(|| async { true })
                .chain(dptree::filter_map(|| Some(1) ))
                .endpoint(|| async {})
        );

    get_locations(&some_tree).iter().for_each(|(name, loc)| println!("{name: <12} @ {loc}"));
}
examples/web_server.rs (line 9)
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async fn main() {
    let web_server = dptree::entry()
        .branch(smiles_handler())
        .branch(sqrt_handler())
        .branch(not_found_handler());

    assert_eq!(
        web_server.dispatch(dptree::deps!["/smile"]).await,
        ControlFlow::Break("🙃".to_owned())
    );
    assert_eq!(
        web_server.dispatch(dptree::deps!["/sqrt 16"]).await,
        ControlFlow::Break("4".to_owned())
    );
    assert_eq!(
        web_server.dispatch(dptree::deps!["/lol"]).await,
        ControlFlow::Break("404 Not Found".to_owned())
    );
}

Executes this handler with a continuation.

Usually, you do not want to call this method by yourself, if you do not write your own handler implementation. If you wish to execute handler without a continuation, take a look at the Handler::dispatch method.

Examples
use dptree::prelude::*;

let handler: Handler<_, _> = dptree::filter(|x: i32| x > 0);

let output = handler.execute(dptree::deps![10], |_| async { ControlFlow::Break("done") }).await;
assert_eq!(output, ControlFlow::Break("done"));

Executes this handler.

Returns ControlFlow::Break when executed successfully, ControlFlow::Continue otherwise.

Examples found in repository?
examples/web_server.rs (line 14)
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async fn main() {
    let web_server = dptree::entry()
        .branch(smiles_handler())
        .branch(sqrt_handler())
        .branch(not_found_handler());

    assert_eq!(
        web_server.dispatch(dptree::deps!["/smile"]).await,
        ControlFlow::Break("🙃".to_owned())
    );
    assert_eq!(
        web_server.dispatch(dptree::deps!["/sqrt 16"]).await,
        ControlFlow::Break("4".to_owned())
    );
    assert_eq!(
        web_server.dispatch(dptree::deps!["/lol"]).await,
        ControlFlow::Break("404 Not Found".to_owned())
    );
}
More examples
Hide additional examples
examples/simple_dispatcher.rs (line 54)
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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);
    }
}
examples/state_machine.rs (line 37)
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
async fn repl(mut state: CommandState, dispatcher: Handler<'static, Store, CommandState>) {
    loop {
        println!("|| Current state is {}", state);
        print!(">> ");
        std::io::stdout().flush().unwrap();

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

        let str = cmd.trim();
        let event = Event::parse(str);

        let new_state = match event {
            Some(event) => match dispatcher.dispatch(dptree::deps![event, state.clone()]).await {
                ControlFlow::Break(new_state) => new_state,
                ControlFlow::Continue(_) => {
                    println!("There is no transition for the event");
                    continue;
                }
            },
            _ => {
                println!("Unknown event");
                continue;
            }
        };

        if new_state == CommandState::Exit {
            return;
        }

        state = new_state;
    }
}
examples/storage.rs (line 24)
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
async fn main() {
    fn assert_num_string_handler(
        expected_num: u32,
        expected_string: &'static str,
    ) -> Endpoint<'static, Store, ()> {
        // The handler requires `u32` and `String` types from the input storage.
        dptree::endpoint(move |num: u32, string: String| async move {
            assert_eq!(num, expected_num);
            assert_eq!(string, expected_string);
        })
    }

    // Init storage with string and num
    let store = Arc::new(dptree::deps![10u32, "Hello".to_owned()]);

    let str_num_handler = assert_num_string_handler(10u32, "Hello");

    str_num_handler.dispatch(store.clone()).await;

    // This will cause a panic because we do not store `Ipv4Addr` in out store.
    let handle = tokio::spawn(async move {
        let ip_handler: Endpoint<_, _> = dptree::endpoint(|ip: Ipv4Addr| async move {
            assert_eq!(ip, Ipv4Addr::new(0, 0, 0, 0));
        });
        ip_handler.dispatch(store.clone()).await;
    });
    let result = handle.await;
    assert!(result.is_err())
}

Returns the set of updates that can be processed by this handler.

Examples found in repository?
examples/descr_locations.rs (line 90)
87
88
89
90
91
fn get_locations<'a, 'b>(
    handler: &'a Handler<'b, impl Send + Sync + 'b, impl Send + Sync + 'b, CollectLocations>,
) -> &'a [(&'static str, &'static Location<'static>)] {
    &handler.description().locations
}

Chain this handler with the filter predicate pred.

Chain this handler with the async filter predicate pred.

Chain this handler with the filter projection proj.

Chain this handler with the async filter projection proj.

Chain this handler with the map projection proj.

Chain this handler with the async map projection proj.

Chain this handler with the inspection function f.

Chain this handler with the async inspection function f.

Chain this handler with the endpoint handler f.

Examples found in repository?
examples/state_machine.rs (line 107)
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
    pub fn begin() -> Transition {
        dptree::case![Event::Begin].endpoint(|| async { CommandState::Active })
    }

    pub fn pause() -> Transition {
        dptree::case![Event::Pause].endpoint(|| async { CommandState::Paused })
    }

    pub fn end() -> Transition {
        dptree::case![Event::End].endpoint(|| async { CommandState::Inactive })
    }

    pub fn resume() -> Transition {
        dptree::case![Event::Resume].endpoint(|| async { CommandState::Active })
    }

    pub fn exit() -> Transition {
        dptree::case![Event::Exit].endpoint(|| async { CommandState::Exit })
    }
More examples
Hide additional examples
examples/web_server.rs (line 29)
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
fn smiles_handler() -> WebHandler {
    dptree::filter(|req: &'static str| req.starts_with("/smile"))
        .endpoint(|| async { "🙃".to_owned() })
}

fn sqrt_handler() -> WebHandler {
    dptree::filter_map(|req: &'static str| {
        if req.starts_with("/sqrt") {
            let (_, n) = req.split_once(' ')?;
            n.parse::<f64>().ok()
        } else {
            None
        }
    })
    .endpoint(|n: f64| async move { format!("{}", n.sqrt()) })
}
examples/simple_dispatcher.rs (line 87)
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
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)
        },
    )
}
examples/descr_locations.rs (line 98)
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
fn main() {
    #[rustfmt::skip]
    let some_tree: Handler<DependencyMap, _, _> = dptree::entry()
        .branch(
            dptree::filter(|| true)
                .endpoint(|| async {})
        )
        .branch(
            dptree::filter_async(|| async { true })
                .chain(dptree::filter_map(|| Some(1) ))
                .endpoint(|| async {})
        );

    get_locations(&some_tree).iter().for_each(|(name, loc)| println!("{name: <12} @ {loc}"));
}

Trait Implementations

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The resulting type after obtaining ownership.

Creates owned data from borrowed data, usually by cloning. Read more

Uses borrowed data to replace owned data, usually by cloning. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.