mirror of
https://github.com/teloxide/teloxide.git
synced 2025-03-14 11:44:04 +01:00
Implement teloxide::handler!
This commit is contained in:
parent
f8438f1772
commit
9cd4bb11b0
1 changed files with 127 additions and 0 deletions
|
@ -179,3 +179,130 @@ where
|
|||
self.storage.clone().remove_dialogue(self.chat_id).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a dialogue FSM transition.
|
||||
///
|
||||
/// This macro expands to a [`dptree::Handler`] that filters your dialogue
|
||||
/// state: if the state enumeration is of a certain variant, the execution
|
||||
/// continues; otherwise, `dptree` will try the next branch.
|
||||
///
|
||||
/// Variants can take the following forms:
|
||||
///
|
||||
/// - `State::MyVariant` for empty variants;
|
||||
/// - `State::MyVariant(param1, ..., paramN)` for function-like variants;
|
||||
/// - `State::MyVariant { param1, ..., paramN }` for `struct`-like variants.
|
||||
///
|
||||
/// In the first case, this macro results in a simple [`dptree::filter`]; in the
|
||||
/// second and third cases, this macro results in [`dptree::filter_map`] that
|
||||
/// passes the payload of `MyVariant` to the next handler if the match occurs.
|
||||
/// (This next handler can be an endpoint or a more complex one.) The payload
|
||||
/// format depend on the form of `MyVariant`:
|
||||
///
|
||||
/// - For `State::MyVariant(param)`, the payload is `param`.
|
||||
/// - For `State::MyVariant(param1, ..., paramN)`, the payload is `(param1,
|
||||
/// ..., paramN)` (where `N` > 1).
|
||||
/// - For `State::MyVariant { param1, ..., paramN }`, the payload is `(param1,
|
||||
/// ..., paramN)`.
|
||||
///
|
||||
/// ## Dependency requirements
|
||||
///
|
||||
/// - Your dialogue state enumeration `State`.
|
||||
#[macro_export]
|
||||
macro_rules! handler {
|
||||
($($variant:ident)::*) => {
|
||||
dptree::filter(|state| matches!(state, $($variant)::*))
|
||||
};
|
||||
($($variant:ident)::* ($param:ident)) => {
|
||||
dptree::filter_map(|state| match state {
|
||||
$($variant)::*($param) => Some($param),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
($($variant:ident)::* ($($param:ident),+ $(,)?)) => {
|
||||
dptree::filter_map(|state| match state {
|
||||
$($variant)::*($($param),+) => Some(($($param),+ ,)),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
($($variant:ident)::* {$($param:ident),+ $(,)?}) => {
|
||||
dptree::filter_map(|state| match state {
|
||||
$($variant)::* { $($param),+ } => Some(($($param),+ ,)),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum State {
|
||||
A,
|
||||
B(i32),
|
||||
C(i32, &'static str),
|
||||
D { foo: i32 },
|
||||
E { foo: i32, bar: &'static str },
|
||||
Other,
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handler_empty_variant() {
|
||||
let input = State::A;
|
||||
let h = handler![State::A].endpoint(|| async move { 123 });
|
||||
|
||||
assert_eq!(h.dispatch(dptree::deps![input]).await, ControlFlow::Break(123));
|
||||
assert!(matches!(h.dispatch(dptree::deps![State::Other]).await, ControlFlow::Continue(_)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handler_single_fn_variant() {
|
||||
let input = State::B(42);
|
||||
let h = handler![State::B(x)].endpoint(|x: i32| async move {
|
||||
assert_eq!(x, 42);
|
||||
123
|
||||
});
|
||||
|
||||
assert_eq!(h.dispatch(dptree::deps![input]).await, ControlFlow::Break(123));
|
||||
assert!(matches!(h.dispatch(dptree::deps![State::Other]).await, ControlFlow::Continue(_)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handler_fn_variant() {
|
||||
let input = State::C(42, "abc");
|
||||
let h = handler![State::C(x, y)].endpoint(|(x, str): (i32, &'static str)| async move {
|
||||
assert_eq!(x, 42);
|
||||
assert_eq!(str, "abc");
|
||||
123
|
||||
});
|
||||
|
||||
assert_eq!(h.dispatch(dptree::deps![input]).await, ControlFlow::Break(123));
|
||||
assert!(matches!(h.dispatch(dptree::deps![State::Other]).await, ControlFlow::Continue(_)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handler_single_struct_variant() {
|
||||
let input = State::D { foo: 42 };
|
||||
let h = handler![State::D { foo }].endpoint(|(x,): (i32,)| async move {
|
||||
assert_eq!(x, 42);
|
||||
123
|
||||
});
|
||||
|
||||
assert_eq!(h.dispatch(dptree::deps![input]).await, ControlFlow::Break(123));
|
||||
assert!(matches!(h.dispatch(dptree::deps![State::Other]).await, ControlFlow::Continue(_)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handler_struct_variant() {
|
||||
let input = State::E { foo: 42, bar: "abc" };
|
||||
let h =
|
||||
handler![State::E { foo, bar }].endpoint(|(x, str): (i32, &'static str)| async move {
|
||||
assert_eq!(x, 42);
|
||||
assert_eq!(str, "abc");
|
||||
123
|
||||
});
|
||||
|
||||
assert_eq!(h.dispatch(dptree::deps![input]).await, ControlFlow::Break(123));
|
||||
assert!(matches!(h.dispatch(dptree::deps![State::Other]).await, ControlFlow::Continue(_)));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue