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
sourceimpl<'a, Input, Output, Descr> Handler<'a, Input, Output, Descr> where
Input: Send + 'a,
Output: 'a,
Descr: HandlerDescription,
impl<'a, Input, Output, Descr> Handler<'a, Input, Output, Descr> where
Input: Send + 'a,
Output: 'a,
Descr: HandlerDescription,
sourcepub fn chain(self, next: Self) -> Self
pub fn chain(self, next: Self) -> Self
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?
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}"));
}
sourcepub fn branch(self, next: Self) -> Self where
Output: Send,
pub fn branch(self, next: Self) -> Self where
Output: Send,
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?
More examples
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())
}
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}"));
}
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())
);
}
sourcepub async fn execute<Cont, ContFut>(
self,
container: Input,
cont: Cont
) -> ControlFlow<Output, Input> where
Cont: FnOnce(Input) -> ContFut,
Cont: Send + Sync + 'a,
ContFut: Future<Output = ControlFlow<Output, Input>> + Send + 'a,
pub async fn execute<Cont, ContFut>(
self,
container: Input,
cont: Cont
) -> ControlFlow<Output, Input> where
Cont: FnOnce(Input) -> ContFut,
Cont: Send + Sync + 'a,
ContFut: Future<Output = ControlFlow<Output, Input>> + Send + 'a,
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"));
sourcepub async fn dispatch(&self, container: Input) -> ControlFlow<Output, Input>
pub async fn dispatch(&self, container: Input) -> ControlFlow<Output, Input>
Executes this handler.
Returns ControlFlow::Break
when executed successfully,
ControlFlow::Continue
otherwise.
Examples found in repository?
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
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);
}
}
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;
}
}
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())
}
sourcepub fn description(&self) -> &Descr
pub fn description(&self) -> &Descr
Returns the set of updates that can be processed by this handler.
sourceimpl<'a, Input, Output, Descr> Handler<'a, Input, Output, Descr> where
Input: Send + 'a,
Output: 'a,
Descr: HandlerDescription,
impl<'a, Input, Output, Descr> Handler<'a, Input, Output, Descr> where
Input: Send + 'a,
Output: 'a,
Descr: HandlerDescription,
sourcepub fn filter<Pred, FnArgs>(
self,
pred: Pred
) -> Handler<'a, Input, Output, Descr> where
Asyncify<Pred>: Injectable<Input, bool, FnArgs> + Send + Sync + 'a,
pub fn filter<Pred, FnArgs>(
self,
pred: Pred
) -> Handler<'a, Input, Output, Descr> where
Asyncify<Pred>: Injectable<Input, bool, FnArgs> + Send + Sync + 'a,
Chain this handler with the filter predicate pred
.
sourcepub fn filter_async<Pred, FnArgs>(
self,
pred: Pred
) -> Handler<'a, Input, Output, Descr> where
Pred: Injectable<Input, bool, FnArgs> + Send + Sync + 'a,
pub fn filter_async<Pred, FnArgs>(
self,
pred: Pred
) -> Handler<'a, Input, Output, Descr> where
Pred: Injectable<Input, bool, FnArgs> + Send + Sync + 'a,
Chain this handler with the async filter predicate pred
.
sourcepub fn filter_map<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Asyncify<Proj>: Injectable<Input, Option<NewType>, Args> + Send + Sync + 'a,
NewType: Send,
pub fn filter_map<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Asyncify<Proj>: Injectable<Input, Option<NewType>, Args> + Send + Sync + 'a,
NewType: Send,
Chain this handler with the filter projection proj
.
sourcepub fn filter_map_async<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Proj: Injectable<Input, Option<NewType>, Args> + Send + Sync + 'a,
NewType: Send,
pub fn filter_map_async<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Proj: Injectable<Input, Option<NewType>, Args> + Send + Sync + 'a,
NewType: Send,
Chain this handler with the async filter projection proj
.
sourcepub fn map<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Asyncify<Proj>: Injectable<Input, NewType, Args> + Send + Sync + 'a,
NewType: Send,
pub fn map<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Asyncify<Proj>: Injectable<Input, NewType, Args> + Send + Sync + 'a,
NewType: Send,
Chain this handler with the map projection proj
.
sourcepub fn map_async<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Proj: Injectable<Input, NewType, Args> + Send + Sync + 'a,
NewType: Send,
pub fn map_async<Proj, NewType, Args>(
self,
proj: Proj
) -> Handler<'a, Input, Output, Descr> where
Input: Insert<NewType> + Clone,
Proj: Injectable<Input, NewType, Args> + Send + Sync + 'a,
NewType: Send,
Chain this handler with the async map projection proj
.
sourcepub fn inspect<F, Args>(self, f: F) -> Handler<'a, Input, Output, Descr> where
Asyncify<F>: Injectable<Input, (), Args> + Send + Sync + 'a,
pub fn inspect<F, Args>(self, f: F) -> Handler<'a, Input, Output, Descr> where
Asyncify<F>: Injectable<Input, (), Args> + Send + Sync + 'a,
Chain this handler with the inspection function f
.
sourcepub fn inspect_async<F, Args>(self, f: F) -> Handler<'a, Input, Output, Descr> where
F: Injectable<Input, (), Args> + Send + Sync + 'a,
pub fn inspect_async<F, Args>(self, f: F) -> Handler<'a, Input, Output, Descr> where
F: Injectable<Input, (), Args> + Send + Sync + 'a,
Chain this handler with the async inspection function f
.
sourcepub fn endpoint<F, FnArgs>(self, f: F) -> Handler<'a, Input, Output, Descr> where
F: Injectable<Input, Output, FnArgs> + Send + Sync + 'a,
pub fn endpoint<F, FnArgs>(self, f: F) -> Handler<'a, Input, Output, Descr> where
F: Injectable<Input, Output, FnArgs> + Send + Sync + 'a,
Chain this handler with the endpoint handler f
.
Examples found in repository?
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
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()) })
}
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)
},
)
}
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
Auto Trait Implementations
impl<'a, Input, Output, Descr = Unspecified> !RefUnwindSafe for Handler<'a, Input, Output, Descr>
impl<'a, Input, Output, Descr> Send for Handler<'a, Input, Output, Descr> where
Descr: Send + Sync,
impl<'a, Input, Output, Descr> Sync for Handler<'a, Input, Output, Descr> where
Descr: Send + Sync,
impl<'a, Input, Output, Descr> Unpin for Handler<'a, Input, Output, Descr>
impl<'a, Input, Output, Descr = Unspecified> !UnwindSafe for Handler<'a, Input, Output, Descr>
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcefn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more