diff --git a/README.md b/README.md
index 21449ef2..01d6edbf 100644
--- a/README.md
+++ b/README.md
@@ -187,20 +187,18 @@ async fn main() {
}
```
-
-
-
-
-
-
-
### Dialogues
-Wanna see more? This is how dialogues management is made in teloxide.
+A dialogue is described by an enumeration, where each variant is one of possible dialogue's states. There are also _transition functions_, which turn a dialogue from one state to another, thereby forming an [FSM].
+
+[FSM]: https://en.wikipedia.org/wiki/Finite-state_machine
+
+States and transition functions are placed into separated modules. For example:
([dialogue_bot/src/states.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/states.rs))
```rust
// Imports are omitted...
+#[derive(Default)]
pub struct StartState;
pub struct ReceiveFullNameState {
@@ -234,77 +232,83 @@ up!(
ReceiveFavouriteMusicState + [favourite_music: FavouriteMusic] -> ExitState,
);
-pub type Dialogue = Coprod!(
- StartState,
- ReceiveFullNameState,
- ReceiveAgeState,
- ReceiveFavouriteMusicState,
-);
+#[derive(SmartDefault, From)]
+pub enum Dialogue {
+ #[default]
+ Start(StartState),
+ ReceiveFullName(ReceiveFullNameState),
+ ReceiveAge(ReceiveAgeState),
+ ReceiveFavouriteMusic(ReceiveFavouriteMusicState),
+}
```
-The [`wrap_dialogue!`](https://docs.rs/teloxide/latest/teloxide/macro.wrap_dialogue.html) macro generates a new-type of `Dialogue` with a default implementation.
+The handy `up!` macro automatically generates functions that complete one state to another by appending a field.
([dialogue_bot/src/transitions.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/transitions.rs))
```rust
// Imports are omitted...
-pub type In = TransitionIn;
-pub type Out = TransitionOut;
-
-pub async fn start(cx: In) -> Out {
- let (cx, dialogue) = cx.unpack();
+pub type Cx = UpdateWithCx;
+pub type Out = TransitionOut;
+async fn start(cx: Cx, state: StartState) -> Out {
cx.answer_str("Let's start! First, what's your full name?").await?;
- next(dialogue.up())
+ next(state.up())
}
-pub async fn receive_full_name(cx: In) -> Out {
- let (cx, dialogue) = cx.unpack();
-
+async fn receive_full_name(cx: Cx, state: ReceiveFullNameState) -> Out {
match cx.update.text_owned() {
Some(full_name) => {
cx.answer_str("What a wonderful name! Your age?").await?;
- next(dialogue.up(full_name))
+ next(state.up(full_name))
}
_ => {
cx.answer_str("Please, enter a text message!").await?;
- next(dialogue)
+ next(state)
}
}
}
-pub async fn receive_age(cx: In) -> Out {
- let (cx, dialogue) = cx.unpack();
-
+async fn receive_age(cx: Cx, state: ReceiveAgeState) -> Out {
match cx.update.text().map(str::parse) {
Some(Ok(age)) => {
cx.answer("Good. Now choose your favourite music:")
.reply_markup(FavouriteMusic::markup())
.send()
.await?;
- next(dialogue.up(age))
+ next(state.up(age))
}
_ => {
cx.answer_str("Please, enter a number!").await?;
- next(dialogue)
+ next(state)
}
}
}
-pub async fn receive_favourite_music(
- cx: In,
+async fn receive_favourite_music(
+ cx: Cx,
+ state: ReceiveFavouriteMusicState,
) -> Out {
- let (cx, dialogue) = cx.unpack();
-
match cx.update.text().map(str::parse) {
Some(Ok(favourite_music)) => {
- cx.answer_str(format!("Fine. {}", dialogue.up(favourite_music)))
+ cx.answer_str(format!("Fine. {}", state.up(favourite_music)))
.await?;
exit()
}
_ => {
cx.answer_str("Please, enter from the keyboard!").await?;
- next(dialogue)
+ next(state)
+ }
+ }
+}
+
+pub async fn dispatch(cx: Cx, dialogue: Dialogue) -> Out {
+ match dialogue {
+ Dialogue::Start(state) => start(cx, state).await,
+ Dialogue::ReceiveFullName(state) => receive_full_name(cx, state).await,
+ Dialogue::ReceiveAge(state) => receive_age(cx, state).await,
+ Dialogue::ReceiveFavouriteMusic(state) => {
+ receive_favourite_music(cx, state).await
}
}
}
@@ -335,6 +339,7 @@ impl FavouriteMusic {
```
+
([dialogue_bot/src/main.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/main.rs))
```rust
// Imports are omitted...
@@ -347,18 +352,14 @@ async fn main() {
let bot = Bot::from_env();
Dispatcher::new(bot)
- .messages_handler(DialogueDispatcher::new(|cx| async move {
- let DialogueWithCx { cx, dialogue } = cx;
-
- // Unwrap without panic because of std::convert::Infallible.
- let Wrapper(dialogue) = dialogue.unwrap();
-
- dispatch!(
- [cx, dialogue] ->
- [start, receive_full_name, receive_age, receive_favourite_music]
- )
- .expect("Something wrong with the bot!")
- }, || Dialogue::inject(StartState)))
+ .messages_handler(DialogueDispatcher::new(
+ |input: TransitionIn| async move {
+ // Unwrap without panic because of std::convert::Infallible.
+ dispatch(input.cx, input.dialogue.unwrap())
+ .await
+ .expect("Something wrong with the bot!")
+ },
+ ))
.dispatch()
.await;
}