From 410c1cc8d8c864b2e7a48940ab104714ae39ae3f Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Mon, 30 Dec 2019 04:46:04 +0600 Subject: [PATCH 01/23] Refactor teloxide::dispatching --- src/bot/execute.rs | 5 +- .../dispatchers/{filter/mod.rs => filter.rs} | 258 +++++++++--------- .../dispatchers/filter/error_policy.rs | 119 -------- src/dispatching/dispatchers/mod.rs | 2 + src/dispatching/error_handler.rs | 120 ++++++++ src/dispatching/filters/mod.rs | 2 + src/dispatching/handler.rs | 57 ++-- src/dispatching/mod.rs | 19 +- src/dispatching/updater.rs | 159 ----------- src/dispatching/updaters.rs | 171 ++++++++++++ 10 files changed, 455 insertions(+), 457 deletions(-) rename src/dispatching/dispatchers/{filter/mod.rs => filter.rs} (56%) delete mode 100644 src/dispatching/dispatchers/filter/error_policy.rs create mode 100644 src/dispatching/error_handler.rs delete mode 100644 src/dispatching/updater.rs create mode 100644 src/dispatching/updaters.rs diff --git a/src/bot/execute.rs b/src/bot/execute.rs index b3c307d5..6368983d 100644 --- a/src/bot/execute.rs +++ b/src/bot/execute.rs @@ -76,10 +76,7 @@ impl Bot { /// # use teloxide::{Bot, requests::payloads::SendAnimation, types::InputFile}; /// # #[tokio::main] async fn main_() { /// let bot = Bot::new("TOKEN"); - /// let payload = SendAnimation::new( - /// 123456, - /// InputFile::Url(String::from("https://example.com")) - /// ); + /// let payload = SendAnimation::new(123456, InputFile::Url(String::from("https://example.com"))); /// bot.execute_multipart(&payload).await; /// # } /// ``` diff --git a/src/dispatching/dispatchers/filter/mod.rs b/src/dispatching/dispatchers/filter.rs similarity index 56% rename from src/dispatching/dispatchers/filter/mod.rs rename to src/dispatching/dispatchers/filter.rs index 89fb093d..d00de234 100644 --- a/src/dispatching/dispatchers/filter/mod.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -1,47 +1,25 @@ +//! A dispatcher based on filters. + use futures::StreamExt; -use async_trait::async_trait; - use crate::{ - dispatching::{ - dispatchers::filter::error_policy::ErrorPolicy, filters::Filter, - handler::Handler, updater::Updater, Dispatcher, - }, + dispatching::{filters::Filter, ErrorHandler, Handler, Updater}, types::{ CallbackQuery, ChosenInlineResult, InlineQuery, Message, Update, UpdateKind, }, }; -pub mod error_policy; +type FilterWithHandler<'a, T, E> = + (Box + 'a>, Box + 'a>); +type FiltersWithHandlers<'a, T, E> = Vec>; -struct FilterAndHandler<'a, T, E> { - filter: Box + 'a>, - handler: Box + 'a>, -} - -impl<'a, T, E> FilterAndHandler<'a, T, E> { - fn new(filter: F, handler: H) -> Self - where - F: Filter + 'a, - H: Handler<'a, T, E> + 'a, - { - FilterAndHandler { - filter: Box::new(filter), - handler: Box::new(handler), - } - } -} - -type FiltersAndHandlers<'a, T, E> = Vec>; - -/// Dispatcher that dispatches updates from telegram. +/// A dispatcher based on filters. /// /// This is 'filter' implementation with following limitations: /// - Error (`E` generic parameter) _must_ implement [`std::fmt::Debug`] /// - All 'handlers' are boxed /// - Handler's fututres are also boxed -/// - All errors from [updater] are ignored (TODO: remove this limitation) /// - All handlers executed in order (this means that in dispatching have 2 /// upadtes it will first execute some handler into complition with first /// update and **then** search for handler for second update, this is probably @@ -89,24 +67,32 @@ type FiltersAndHandlers<'a, T, E> = Vec>; /// /// [`std::fmt::Debug`]: std::fmt::Debug /// [updater]: crate::dispatching::updater -pub struct FilterDispatcher<'a, E, Ep> { - message_handlers: FiltersAndHandlers<'a, Message, E>, - edited_message_handlers: FiltersAndHandlers<'a, Message, E>, - channel_post_handlers: FiltersAndHandlers<'a, Message, E>, - edited_channel_post_handlers: FiltersAndHandlers<'a, Message, E>, - inline_query_handlers: FiltersAndHandlers<'a, InlineQuery, E>, +pub struct FilterDispatcher<'a, E, Eh> { + message_handlers: FiltersWithHandlers<'a, Message, E>, + edited_message_handlers: FiltersWithHandlers<'a, Message, E>, + channel_post_handlers: FiltersWithHandlers<'a, Message, E>, + edited_channel_post_handlers: FiltersWithHandlers<'a, Message, E>, + inline_query_handlers: FiltersWithHandlers<'a, InlineQuery, E>, chosen_inline_result_handlers: - FiltersAndHandlers<'a, ChosenInlineResult, E>, - callback_query_handlers: FiltersAndHandlers<'a, CallbackQuery, E>, - error_policy: Ep, + FiltersWithHandlers<'a, ChosenInlineResult, E>, + callback_query_handlers: FiltersWithHandlers<'a, CallbackQuery, E>, + error_handler: Eh, } -impl<'a, E, Ep> FilterDispatcher<'a, E, Ep> -where - Ep: ErrorPolicy, - E: std::fmt::Debug, // TODO: Is this really necessary? -{ - pub fn new(error_policy: Ep) -> Self { +/// An error produced either from [`Updater`] or [`Handler`]. +/// +/// [`Updater`]: crate::dispatching::Updater +/// [`Handler`]: crate::dispatching::Handler +pub enum ErrorKind { + FromUpdater(E1), + FromHandler(E2), +} + +impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { + pub fn new(error_handler: Eh) -> Self + where + Eh: ErrorHandler>, + { FilterDispatcher { message_handlers: Vec::new(), edited_message_handlers: Vec::new(), @@ -115,37 +101,37 @@ where inline_query_handlers: Vec::new(), chosen_inline_result_handlers: Vec::new(), callback_query_handlers: Vec::new(), - error_policy, + error_handler, } } pub fn message_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler<'a, Message, E> + 'a, + H: Handler + 'a, { self.message_handlers - .push(FilterAndHandler::new(filter, handler)); + .push((Box::new(filter), Box::new(handler))); self } pub fn edited_message_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler<'a, Message, E> + 'a, + H: Handler + 'a, { self.edited_message_handlers - .push(FilterAndHandler::new(filter, handler)); + .push((Box::new(filter), Box::new(handler))); self } pub fn channel_post_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler<'a, Message, E> + 'a, + H: Handler + 'a, { self.channel_post_handlers - .push(FilterAndHandler::new(filter, handler)); + .push((Box::new(filter), Box::new(handler))); self } @@ -156,20 +142,20 @@ where ) -> Self where F: Filter + 'a, - H: Handler<'a, Message, E> + 'a, + H: Handler + 'a, { self.edited_channel_post_handlers - .push(FilterAndHandler::new(filter, handler)); + .push((Box::new(filter), Box::new(handler))); self } pub fn inline_query_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler<'a, InlineQuery, E> + 'a, + H: Handler + 'a, { self.inline_query_handlers - .push(FilterAndHandler::new(filter, handler)); + .push((Box::new(filter), Box::new(handler))); self } @@ -180,97 +166,121 @@ where ) -> Self where F: Filter + 'a, - H: Handler<'a, ChosenInlineResult, E> + 'a, + H: Handler + 'a, { self.chosen_inline_result_handlers - .push(FilterAndHandler::new(filter, handler)); + .push((Box::new(filter), Box::new(handler))); self } pub fn callback_query_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler<'a, CallbackQuery, E> + 'a, + H: Handler + 'a, { self.callback_query_handlers - .push(FilterAndHandler::new(filter, handler)); + .push((Box::new(filter), Box::new(handler))); self } - // TODO: Can someone simplify this? - pub async fn dispatch(&mut self, updates: U) + pub async fn dispatch(&mut self, updater: U) where - U: Updater + 'a, + U: Updater + 'a, + Eh: ErrorHandler>, { - updates - .for_each(|res| { - async { - let Update { kind, id } = match res { - Ok(upd) => upd, - _ => return, // TODO: proper error handling - }; + updater + .for_each(|res| async { + let Update { kind, id } = match res { + Ok(upd) => upd, + Err(err) => { + self.error_handler + .handle_error(ErrorKind::FromUpdater(err)); + return; + } + }; - log::debug!( - "Handled update#{id:?}: {kind:?}", - id = id, - kind = kind - ); + log::debug!( + "Handled update#{id:?}: {kind:?}", + id = id, + kind = kind + ); - match kind { - UpdateKind::Message(mes) => { - self.handle(mes, &self.message_handlers).await - } - UpdateKind::EditedMessage(mes) => { - self.handle(mes, &self.edited_message_handlers) - .await; - } - UpdateKind::ChannelPost(post) => { - self.handle(post, &self.channel_post_handlers) - .await; - } - UpdateKind::EditedChannelPost(post) => { - self.handle( - post, - &self.edited_channel_post_handlers, - ) - .await; - } - UpdateKind::InlineQuery(query) => { - self.handle(query, &self.inline_query_handlers) - .await; - } - UpdateKind::ChosenInlineResult(result) => { - self.handle( - result, - &self.chosen_inline_result_handlers, - ) - .await; - } - UpdateKind::CallbackQuery(callback) => { - self.handle( - callback, - &self.callback_query_handlers, - ) - .await; - } + match kind { + UpdateKind::Message(mes) => { + Self::handle( + mes, + &mut self.message_handlers, + &mut self.error_handler, + ) + .await + } + UpdateKind::EditedMessage(mes) => { + Self::handle( + mes, + &mut self.edited_message_handlers, + &mut self.error_handler, + ) + .await; + } + UpdateKind::ChannelPost(post) => { + Self::handle( + post, + &mut self.channel_post_handlers, + &mut self.error_handler, + ) + .await; + } + UpdateKind::EditedChannelPost(post) => { + Self::handle( + post, + &mut self.edited_channel_post_handlers, + &mut self.error_handler, + ) + .await; + } + UpdateKind::InlineQuery(query) => { + Self::handle( + query, + &mut self.inline_query_handlers, + &mut self.error_handler, + ) + .await; + } + UpdateKind::ChosenInlineResult(result) => { + Self::handle( + result, + &mut self.chosen_inline_result_handlers, + &mut self.error_handler, + ) + .await; + } + UpdateKind::CallbackQuery(callback) => { + Self::handle( + callback, + &mut self.callback_query_handlers, + &mut self.error_handler, + ) + .await; } } }) .await; } - #[allow(clippy::ptr_arg)] // TODO: proper fix - async fn handle( - &self, + async fn handle( update: T, - handlers: &FiltersAndHandlers<'a, T, E>, + handlers: &mut FiltersWithHandlers<'a, T, E2>, + error_handler: &mut Eh, ) where T: std::fmt::Debug, + Eh: ErrorHandler>, { for x in handlers { - if x.filter.test(&update) { - if let Err(err) = x.handler.handle(update).await { - self.error_policy.handle_error(err).await + if x.0.test(&update) { + if let Err(err) = x.1.handle(update).await { + error_handler + .handle_error(ErrorKind::FromHandler(err)) + .await } return; @@ -281,18 +291,6 @@ where } } -#[async_trait(? Send)] -impl<'a, U, E, Ep> Dispatcher<'a, U> for FilterDispatcher<'a, E, Ep> -where - E: std::fmt::Debug, - U: Updater + 'a, - Ep: ErrorPolicy, -{ - async fn dispatch(&'a mut self, updater: U) { - FilterDispatcher::dispatch(self, updater).await - } -} - #[cfg(test)] mod tests { use std::{ diff --git a/src/dispatching/dispatchers/filter/error_policy.rs b/src/dispatching/dispatchers/filter/error_policy.rs deleted file mode 100644 index 8d40bbb2..00000000 --- a/src/dispatching/dispatchers/filter/error_policy.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Infallible used here instead of `!` to be compatible with rust <1.41 -use std::{convert::Infallible, future::Future, pin::Pin}; - -use async_trait::async_trait; - -/// Implementors of this trait are treated as error-handlers. -#[async_trait] -pub trait ErrorPolicy { - async fn handle_error(&self, error: E) - where - E: 'async_trait; -} - -/// Error policy that silently ignores all errors -/// -/// ## Example -/// ``` -/// # #[tokio::main] -/// # async fn main_() { -/// use teloxide::dispatching::dispatchers::filter::error_policy::{ -/// ErrorPolicy, Ignore, -/// }; -/// -/// Ignore.handle_error(()).await; -/// Ignore.handle_error(404).await; -/// Ignore.handle_error(String::from("error")).await; -/// # } -/// ``` -pub struct Ignore; - -#[async_trait] -impl ErrorPolicy for Ignore -where - E: Send, -{ - async fn handle_error(&self, _: E) - where - E: 'async_trait, - { - } -} - -/// Error policy that silently ignores all errors that can never happen (e.g.: -/// [`!`] or [`Infallible`]) -/// -/// ## Examples -/// ``` -/// # #[tokio::main] -/// # async fn main_() { -/// use std::convert::{TryInto, Infallible}; -/// -/// use teloxide::dispatching::dispatchers::filter::error_policy::{ -/// ErrorPolicy, -/// IgnoreSafe, -/// }; -/// -/// let result: Result = "str".try_into(); -/// match result { -/// Ok(string) => println!("{}", string), -/// Err(inf) => IgnoreSafe.handle_error(inf).await, -/// } -/// -/// IgnoreSafe.handle_error(return).await; // return type of `return` is `!` (aka never) -/// # } -/// ``` -/// -/// ```compile_fail -/// use teloxide::dispatching::dispatchers::filter::error_policy::{ -/// ErrorPolicy, IgnoreSafe, -/// }; -/// -/// IgnoreSafe.handle_error(0); -/// ``` -/// -/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html -/// [`Infallible`]: std::convert::Infallible -pub struct IgnoreSafe; - -#[allow(unreachable_code)] -#[async_trait] -impl ErrorPolicy for IgnoreSafe { - async fn handle_error(&self, _: Infallible) - where - Infallible: 'async_trait, - { - } -} - -/// Implementation of `ErrorPolicy` for `async fn`s -/// -/// ## Example -/// ``` -/// # #[tokio::main] -/// # async fn main_() { -/// use teloxide::dispatching::dispatchers::filter::error_policy::ErrorPolicy; -/// -/// let closure = |e: i32| async move { eprintln!("Error code{}", e) }; -/// -/// closure.handle_error(404).await; -/// # } -/// ``` -impl ErrorPolicy for F -where - F: Fn(E) -> Fut + Sync, - Fut: Future + Send, - E: Send, -{ - fn handle_error<'s, 'async_trait>( - &'s self, - error: E, - ) -> Pin + Send + 'async_trait>> - where - 's: 'async_trait, - Self: 'async_trait, - E: 'async_trait, - { - Box::pin(async move { self(error).await }) - } -} diff --git a/src/dispatching/dispatchers/mod.rs b/src/dispatching/dispatchers/mod.rs index 1ec549e1..f3a34672 100644 --- a/src/dispatching/dispatchers/mod.rs +++ b/src/dispatching/dispatchers/mod.rs @@ -1,3 +1,5 @@ +//! Different types of dispatchers. + pub use filter::FilterDispatcher; pub mod filter; diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handler.rs new file mode 100644 index 00000000..ffafe3ed --- /dev/null +++ b/src/dispatching/error_handler.rs @@ -0,0 +1,120 @@ +//! Error handlers. +//! +//! Looks quite strange for now, but with stabilised asynchronous traits it +//! should be prettier. + +// Infallible used here instead of `!` to be compatible with rust <1.41. +use std::{convert::Infallible, future::Future, pin::Pin}; + +/// A handler of an error. +pub trait ErrorHandler { + fn handle_error<'a>( + &'a mut self, + error: E, + ) -> Pin + 'a>> + where + E: 'a; +} + +/// A handler that silently ignores all errors. +/// +/// ## Example +/// ``` +/// # #[tokio::main] +/// # async fn main_() { +/// use teloxide::dispatching::error_handler::{ErrorHandler, Ignore}; +/// +/// Ignore.handle_error(()).await; +/// Ignore.handle_error(404).await; +/// Ignore.handle_error(String::from("error")).await; +/// # } +/// ``` +pub struct Ignore; + +impl ErrorHandler for Ignore { + #[must_use] + fn handle_error<'a>( + &'a mut self, + _: E, + ) -> Pin + 'a>> + where + E: 'a, + { + Box::pin(async {}) + } +} + +/// An error handler that silently ignores all errors that can never happen +/// (e.g.: [`!`] or [`Infallible`]). +/// +/// ## Examples +/// ``` +/// # #[tokio::main] +/// # async fn main_() { +/// use std::convert::{Infallible, TryInto}; +/// +/// use teloxide::dispatching::error_handler::{ErrorHandler, IgnoreSafe}; +/// +/// let result: Result = "str".try_into(); +/// match result { +/// Ok(string) => println!("{}", string), +/// Err(inf) => IgnoreSafe.handle_error(inf).await, +/// } +/// +/// IgnoreSafe.handle_error(return).await; // return type of `return` is `!` (aka never) +/// # } +/// ``` +/// +/// ```compile_fail +/// use teloxide::dispatching::dispatchers::filter::error_policy::{ +/// ErrorPolicy, IgnoreSafe, +/// }; +/// +/// IgnoreSafe.handle_error(0); +/// ``` +/// +/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html +/// [`Infallible`]: std::convert::Infallible +pub struct IgnoreSafe; + +#[allow(unreachable_code)] +impl ErrorHandler for IgnoreSafe { + fn handle_error<'a>( + &'a mut self, + _: Infallible, + ) -> Pin + 'a>> + where + Infallible: 'a, + { + Box::pin(async {}) + } +} + +/// The implementation of `ErrorHandler` for `Fn(error) -> Future`. +/// +/// ## Example +/// ``` +/// # #[tokio::main] +/// # async fn main_() { +/// use teloxide::dispatching::error_handler::ErrorHandler; +/// +/// let mut closure = |e: i32| async move { eprintln!("Error code{}", e) }; +/// +/// closure.handle_error(404).await; +/// # } +/// ``` +impl ErrorHandler for F +where + F: FnMut(E) -> Fut, + Fut: Future, +{ + fn handle_error<'a>( + &'a mut self, + error: E, + ) -> Pin + 'a>> + where + E: 'a, + { + Box::pin(async move { self(error).await }) + } +} diff --git a/src/dispatching/filters/mod.rs b/src/dispatching/filters/mod.rs index b3f9237f..4f8207a8 100644 --- a/src/dispatching/filters/mod.rs +++ b/src/dispatching/filters/mod.rs @@ -1,3 +1,5 @@ +//! Filters of messages. + pub use main::*; pub use command::*; diff --git a/src/dispatching/handler.rs b/src/dispatching/handler.rs index 9ce74580..0ecbb846 100644 --- a/src/dispatching/handler.rs +++ b/src/dispatching/handler.rs @@ -1,44 +1,33 @@ use std::{future::Future, pin::Pin}; -use futures::FutureExt; - -pub type HandlerResult = Result<(), E>; - -/// Asynchronous handler for event `T` (like `&self, I -> Future` fn) -pub trait Handler<'a, T, E> { - fn handle( - &self, +/// A handler of a successful value. +pub trait Handler { + #[must_use] + fn handle<'a>( + &'a mut self, value: T, - ) -> Pin> + 'a>>; + ) -> Pin> + 'a>> + where + T: 'a; } -pub trait IntoHandlerResult { - fn into_hr(self) -> HandlerResult; -} - -impl IntoHandlerResult for () { - fn into_hr(self) -> HandlerResult { - Ok(()) - } -} - -impl IntoHandlerResult for HandlerResult { - fn into_hr(self) -> HandlerResult { - self - } -} - -impl<'a, F, Fut, R, T, E> Handler<'a, T, E> for F +/// The implementation of `Handler` for `Fn(U) -> Future`. +/// +/// Looks quite strange for now, but with stabilised asynchronous traits it +/// should be prettier. +impl Handler for F where - F: Fn(T) -> Fut, - Fut: Future + 'a, - R: IntoHandlerResult + 'a, - E: 'a, + F: FnMut(T) -> Fut, + Fut: Future>, { - fn handle( - &self, + fn handle<'a>( + &'a mut self, value: T, - ) -> Pin> + 'a>> { - Box::pin(self(value).map(IntoHandlerResult::into_hr)) + ) -> Pin + 'a>> + where + T: 'a, + { + Box::pin(async move { self(value).await }) } } diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index c239778f..5eb6e445 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -1,15 +1,12 @@ //! Update dispatching. -use async_trait::async_trait; +pub mod dispatchers; +pub mod error_handler; +pub mod filters; +mod handler; +pub mod updaters; + +pub use error_handler::ErrorHandler; pub use filters::Filter; pub use handler::Handler; - -pub mod dispatchers; -pub mod filters; -pub mod handler; -pub mod updater; - -#[async_trait(? Send)] -pub trait Dispatcher<'a, U> { - async fn dispatch(&'a mut self, updater: U); -} +pub use updaters::Updater; diff --git a/src/dispatching/updater.rs b/src/dispatching/updater.rs deleted file mode 100644 index fdf4aaf1..00000000 --- a/src/dispatching/updater.rs +++ /dev/null @@ -1,159 +0,0 @@ -use std::{ - pin::Pin, - task::{Context, Poll}, -}; - -use futures::{stream, Stream, StreamExt}; -use pin_project::pin_project; - -use crate::{bot::Bot, types::Update, RequestError}; - -// Currently just a placeholder, but I'll add here some methods -/// Updater is stream of updates. -/// -/// Telegram supports 2 ways of [getting updates]: [long polling](Long Polling) -/// and webhook -/// -/// ## Long Polling -/// -/// In long polling ([wiki]) you just call [GetUpdates] every N seconds. -/// -/// #### Example: -/// -///
-///     tg                           bot
-///      |                            |
-///      |<---------------------------| Updates? (GetUpdates call)
-///      ↑                            ↑
-///      |          timeout^1         |
-///      ↓                            ↓
-/// Nope |--------------------------->|
-///      ↑                            ↑
-///      | delay between GetUpdates^2 |
-///      ↓                            ↓
-///      |<---------------------------| Updates?
-///      ↑                            ↑
-///      |          timeout^3         |
-///      ↓                            ↓
-/// Yes  |-------[updates 0, 1]------>|
-///      ↑                            ↑
-///      |           delay            |
-///      ↓                            ↓
-///      |<-------[offset = 1]--------| Updates?^4
-///      ↑                            ↑
-///      |           timeout          |
-///      ↓                            ↓
-/// Yes  |---------[update 2]-------->|
-///      ↑                            ↑
-///      |           delay            |
-///      ↓                            ↓
-///      |<-------[offset = 2]--------| Updates?
-///      ↑                            ↑
-///      |           timeout          |
-///      ↓                            ↓
-/// Nope |--------------------------->|
-///      ↑                            ↑
-///      |           delay            |
-///      ↓                            ↓
-///      |<-------[offset = 2]--------| Updates?
-///      ↑                            ↑
-///      |           timeout          |
-///      ↓                            ↓
-/// Nope |--------------------------->|
-///      ↑                            ↑
-///      |           delay            |
-///      ↓                            ↓
-///      |<-------[offset = 2]--------| Updates?
-///      ↑                            ↑
-///      |           timeout          |
-///      ↓                            ↓
-/// Yes  |-------[updates 2..5]------>|
-///      ↑                            ↑
-///      |           delay            |
-///      ↓                            ↓
-///      |<-------[offset = 5]--------| Updates?
-///      ↑                            ↑
-///      |           timeout          |
-///      ↓                            ↓
-/// Nope |--------------------------->|
-///      |                            |
-///      ~    and so on, and so on    ~
-/// 
-/// -/// ^1 Timeout can be even 0 -/// (this is also called short polling), -/// but you should use it **only** for testing purposes -/// -/// ^2 Large delays will cause in bot lags, -/// so delay shouldn't exceed second. -/// -/// ^3 Note that if telegram already have updates for -/// you it will answer you **without** waiting for a timeout -/// -/// ^4 `offset = N` means that we've already received -/// updates `0..=N` -/// -/// [GetUpdates]: crate::requests::payloads::GetUpdates -/// [getting updates]: https://core.telegram.org/bots/api#getting-updates -/// [wiki]: https://en.wikipedia.org/wiki/Push_technology#Long_polling -pub trait Updater: - Stream::Error>> -{ - type Error; -} - -#[pin_project] -pub struct StreamUpdater { - #[pin] - stream: S, -} - -impl StreamUpdater { - pub fn new(stream: S) -> Self { - Self { stream } - } -} - -impl Stream for StreamUpdater -where - S: Stream>, -{ - type Item = Result; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.project().stream.poll_next(cx) - } -} - -impl Updater for StreamUpdater -where - S: Stream>, -{ - type Error = E; -} - -pub fn polling<'a>(bot: &'a Bot) -> impl Updater + 'a { - let stream = stream::unfold((bot, 0), |(bot, mut offset)| async move { - let updates = match bot.get_updates().offset(offset).send().await { - Ok(updates) => { - if let Some(upd) = updates.last() { - offset = upd.id + 1; - } - updates.into_iter().map(Ok).collect::>() - } - Err(err) => vec![Err(err)], - }; - Some((stream::iter(updates), (bot, offset))) - }) - .flatten(); - - StreamUpdater { stream } -} - -// TODO implement webhook (this actually require webserver and probably we -// should add cargo feature that adds webhook) -//pub fn webhook<'a>(bot: &'a cfg: WebhookConfig) -> Updater> + 'a> {} diff --git a/src/dispatching/updaters.rs b/src/dispatching/updaters.rs new file mode 100644 index 00000000..5f715a0b --- /dev/null +++ b/src/dispatching/updaters.rs @@ -0,0 +1,171 @@ +//! Receiving updates from Telegram. +//! +//! The key trait here is [`Updater`]. You can get it by these functions: +//! +//! - [`polling_basic`], which returns a default long polling updater. +//! - [`polling_advanced`], which returns a long/short polling updater with +//! your configuration. +//! +//! And then you can pass it directly to a dispatcher. +//! +//! Telegram supports two ways of [getting updates]: [long]/[short] polling and +//! [webhook]. +//! +//! # Long Polling +//! +//! In long polling, you just call [`Box::get_updates`] every N seconds. +//! +//! ## Example +//! +//!
+//!     tg                           bot
+//!      |                            |
+//!      |<---------------------------| Updates? (Bot::get_updates call)
+//!      ↑                            ↑
+//!      |          timeout^1         |
+//!      ↓                            ↓
+//! Nope |--------------------------->|
+//!      ↑                            ↑
+//!      | delay between Bot::get_updates^2 |
+//!      ↓                            ↓
+//!      |<---------------------------| Updates?
+//!      ↑                            ↑
+//!      |          timeout^3         |
+//!      ↓                            ↓
+//! Yes  |-------[updates 0, 1]------>|
+//!      ↑                            ↑
+//!      |           delay            |
+//!      ↓                            ↓
+//!      |<-------[offset = 1]--------| Updates?^4
+//!      ↑                            ↑
+//!      |           timeout          |
+//!      ↓                            ↓
+//! Yes  |---------[update 2]-------->|
+//!      ↑                            ↑
+//!      |           delay            |
+//!      ↓                            ↓
+//!      |<-------[offset = 2]--------| Updates?
+//!      ↑                            ↑
+//!      |           timeout          |
+//!      ↓                            ↓
+//! Nope |--------------------------->|
+//!      ↑                            ↑
+//!      |           delay            |
+//!      ↓                            ↓
+//!      |<-------[offset = 2]--------| Updates?
+//!      ↑                            ↑
+//!      |           timeout          |
+//!      ↓                            ↓
+//! Nope |--------------------------->|
+//!      ↑                            ↑
+//!      |           delay            |
+//!      ↓                            ↓
+//!      |<-------[offset = 2]--------| Updates?
+//!      ↑                            ↑
+//!      |           timeout          |
+//!      ↓                            ↓
+//! Yes  |-------[updates 2..5]------>|
+//!      ↑                            ↑
+//!      |           delay            |
+//!      ↓                            ↓
+//!      |<-------[offset = 5]--------| Updates?
+//!      ↑                            ↑
+//!      |           timeout          |
+//!      ↓                            ↓
+//! Nope |--------------------------->|
+//!      |                            |
+//!      ~    and so on, and so on    ~
+//! 
+//! +//! ^1 A timeout can be even 0 +//! (this is also called short polling), +//! but you should use it **only** for testing purposes. +//! +//! ^2 Large delays will cause in bot lags, +//! so delay shouldn't exceed second. +//! +//! ^3 Note that if Telegram already have updates for +//! you it will answer you **without** waiting for a timeout. +//! +//! ^4 `offset = N` means that we've already received +//! updates `0..=N`. +//! +//! [`Updater`]: Updater +//! [`polling_basic`]: polling_basic +//! [`polling_advanced`]: polling_advanced +//! [`Dispatcher`]: crate::dispatching::Dispatcher::dispatch +//! [`Box::get_updates`]: crate::Bot::get_updates +//! [getting updates]: https://core.telegram.org/bots/api#getting-updates +//! [long]: https://en.wikipedia.org/wiki/Push_technology#Long_polling +//! [short]: https://en.wikipedia.org/wiki/Polling_(computer_science) +//! [webhook]: https://en.wikipedia.org/wiki/Webhook + +use futures::{stream, Stream, StreamExt}; + +use crate::{ + bot::Bot, requests::payloads::AllowedUpdate, types::Update, RequestError, +}; +use std::{convert::TryInto, time::Duration}; + +/// A generic updater. +pub trait Updater: Stream> { + // TODO: add some methods here (.shutdown(), etc). +} +impl Updater for S where S: Stream> {} + +/// Returns a long polling updater with the default configuration. +/// +/// It is the same as calling [`polling_advanced`] with `timeout` of 30 seconds, +/// `limit=100` and receive all kinds of updates. +/// +/// [`polling_advanced`]: polling_advanced +pub fn polling_basic(bot: &Bot) -> impl Updater + '_ { + polling_advanced::<&[_]>(bot, Duration::from_secs(30), 100, &[]) +} + +/// Returns a long/short polling updater with some additional options. +/// +/// - `bot`: Using this bot, the returned updater will receive updates. +/// - `timeout`: A timeout for polling. +/// - `limit`: Limits the number of updates to be retrieved at once. Values +/// between 1—100 are accepted. +/// - `allowed_updates`: A list the types of updates you want to receive. +pub fn polling_advanced<'a, A>( + bot: &'a Bot, + timeout: Duration, + limit: u8, + allowed_updates: A, +) -> impl Updater + 'a +where + A: Into<&'a [AllowedUpdate]>, +{ + let mut allowed_updates = Some(allowed_updates.into()); + + stream::unfold((bot, 0), move |(bot, mut offset)| async move { + let updates = bot + .get_updates() + .offset(offset) + .timeout(timeout.as_secs().try_into().expect("timeout is too big")) + .limit(limit) + .allowed_updates(allowed_updates.take().unwrap_or(&[])) + .send() + .await + .map_or_else( + |err| vec![Err(err)], + |updates| { + if let Some(upd) = updates.last() { + offset = upd.id + 1; + } + updates.into_iter().map(Ok).collect::>() + }, + ); + + Some((stream::iter(updates), (bot, offset))) + }) + .flatten() +} + +// TODO implement webhook (this actually require webserver and probably we +// should add cargo feature that adds webhook) +//pub fn webhook<'a>(bot: &'a cfg: WebhookConfig) -> Updater> + 'a> {} From 117094e9d0388959fb4229199ec7d63686059395 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Mon, 30 Dec 2019 17:27:30 +0600 Subject: [PATCH 02/23] Improve docs of FilterDispatcher --- src/dispatching/dispatchers/filter.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index d00de234..5abbf159 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -16,14 +16,19 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// A dispatcher based on filters. /// -/// This is 'filter' implementation with following limitations: -/// - Error (`E` generic parameter) _must_ implement [`std::fmt::Debug`] -/// - All 'handlers' are boxed -/// - Handler's fututres are also boxed -/// - All handlers executed in order (this means that in dispatching have 2 -/// upadtes it will first execute some handler into complition with first -/// update and **then** search for handler for second update, this is probably -/// wrong) +/// Filters and handlers are executed in order of registering. The pseudocode +/// looks like this: +/// +/// ``` +/// for pair in handlers_and_filters { +/// if pair.filter.test(update) { +/// pair.handle(update); +/// return; +/// } +/// } +/// +/// log("unhandeled update: " + update); +/// ``` /// /// ## Examples /// From 51b52b831709a5859fe5fa20df6f742db2f2676d Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Mon, 30 Dec 2019 18:51:49 +0600 Subject: [PATCH 03/23] Fix one error --- src/dispatching/updaters.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/dispatching/updaters.rs b/src/dispatching/updaters.rs index 5f715a0b..3ee17ec9 100644 --- a/src/dispatching/updaters.rs +++ b/src/dispatching/updaters.rs @@ -142,7 +142,7 @@ where let mut allowed_updates = Some(allowed_updates.into()); stream::unfold((bot, 0), move |(bot, mut offset)| async move { - let updates = bot + let updates = match bot .get_updates() .offset(offset) .timeout(timeout.as_secs().try_into().expect("timeout is too big")) @@ -150,15 +150,15 @@ where .allowed_updates(allowed_updates.take().unwrap_or(&[])) .send() .await - .map_or_else( - |err| vec![Err(err)], - |updates| { - if let Some(upd) = updates.last() { - offset = upd.id + 1; - } - updates.into_iter().map(Ok).collect::>() - }, - ); + { + Err(err) => vec![Err(err)], + Ok(updates) => { + if let Some(upd) = updates.last() { + offset = upd.id + 1; + } + updates.into_iter().map(Ok).collect::>() + } + }; Some((stream::iter(updates), (bot, offset))) }) From 10a7bd6b55bda7aec82e030fa70ac506711106ef Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Mon, 30 Dec 2019 21:58:10 +0600 Subject: [PATCH 04/23] Fix the compilation error --- src/dispatching/dispatchers/filter.rs | 43 ++++++++++++--------------- src/dispatching/error_handler.rs | 10 +++---- src/dispatching/handler.rs | 6 ++-- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index 5abbf159..b76b0a3a 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -214,56 +214,56 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { UpdateKind::Message(mes) => { Self::handle( mes, - &mut self.message_handlers, - &mut self.error_handler, + &self.message_handlers, + &self.error_handler, ) .await } UpdateKind::EditedMessage(mes) => { Self::handle( mes, - &mut self.edited_message_handlers, - &mut self.error_handler, + &self.edited_message_handlers, + &self.error_handler, ) .await; } UpdateKind::ChannelPost(post) => { Self::handle( post, - &mut self.channel_post_handlers, - &mut self.error_handler, + &self.channel_post_handlers, + &self.error_handler, ) .await; } UpdateKind::EditedChannelPost(post) => { Self::handle( post, - &mut self.edited_channel_post_handlers, - &mut self.error_handler, + &self.edited_channel_post_handlers, + &self.error_handler, ) .await; } UpdateKind::InlineQuery(query) => { Self::handle( query, - &mut self.inline_query_handlers, - &mut self.error_handler, + &self.inline_query_handlers, + &self.error_handler, ) .await; } UpdateKind::ChosenInlineResult(result) => { Self::handle( result, - &mut self.chosen_inline_result_handlers, - &mut self.error_handler, + &self.chosen_inline_result_handlers, + &self.error_handler, ) .await; } UpdateKind::CallbackQuery(callback) => { Self::handle( callback, - &mut self.callback_query_handlers, - &mut self.error_handler, + &self.callback_query_handlers, + &self.error_handler, ) .await; } @@ -274,8 +274,8 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { async fn handle( update: T, - handlers: &mut FiltersWithHandlers<'a, T, E2>, - error_handler: &mut Eh, + handlers: &FiltersWithHandlers<'a, T, E2>, + error_handler: &Eh, ) where T: std::fmt::Debug, Eh: ErrorHandler>, @@ -303,12 +303,8 @@ mod tests { sync::atomic::{AtomicI32, Ordering}, }; - use futures::Stream; - use crate::{ - dispatching::{ - dispatchers::filter::FilterDispatcher, updater::StreamUpdater, - }, + dispatching::{dispatchers::filter::FilterDispatcher, Updater}, types::{ Chat, ChatKind, ForwardKind, MediaKind, Message, MessageKind, Sender, Update, UpdateKind, User, @@ -378,10 +374,9 @@ mod tests { } } - fn one_message_updater( - ) -> StreamUpdater>> { + fn one_message_updater() -> impl Updater { use futures::{future::ready, stream}; - StreamUpdater::new(stream::once(ready(Ok(message_update())))) + stream::once(ready(Ok(message_update()))) } } diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handler.rs index ffafe3ed..d57602b9 100644 --- a/src/dispatching/error_handler.rs +++ b/src/dispatching/error_handler.rs @@ -9,7 +9,7 @@ use std::{convert::Infallible, future::Future, pin::Pin}; /// A handler of an error. pub trait ErrorHandler { fn handle_error<'a>( - &'a mut self, + &'a self, error: E, ) -> Pin + 'a>> where @@ -34,7 +34,7 @@ pub struct Ignore; impl ErrorHandler for Ignore { #[must_use] fn handle_error<'a>( - &'a mut self, + &'a self, _: E, ) -> Pin + 'a>> where @@ -80,7 +80,7 @@ pub struct IgnoreSafe; #[allow(unreachable_code)] impl ErrorHandler for IgnoreSafe { fn handle_error<'a>( - &'a mut self, + &'a self, _: Infallible, ) -> Pin + 'a>> where @@ -105,11 +105,11 @@ impl ErrorHandler for IgnoreSafe { /// ``` impl ErrorHandler for F where - F: FnMut(E) -> Fut, + F: Fn(E) -> Fut, Fut: Future, { fn handle_error<'a>( - &'a mut self, + &'a self, error: E, ) -> Pin + 'a>> where diff --git a/src/dispatching/handler.rs b/src/dispatching/handler.rs index 0ecbb846..e919594c 100644 --- a/src/dispatching/handler.rs +++ b/src/dispatching/handler.rs @@ -4,7 +4,7 @@ use std::{future::Future, pin::Pin}; pub trait Handler { #[must_use] fn handle<'a>( - &'a mut self, + &'a self, value: T, ) -> Pin> + 'a>> where @@ -18,11 +18,11 @@ pub trait Handler { /// should be prettier. impl Handler for F where - F: FnMut(T) -> Fut, + F: Fn(T) -> Fut, Fut: Future>, { fn handle<'a>( - &'a mut self, + &'a self, value: T, ) -> Pin + 'a>> where From e815c0945d243973fa2f46a469fcb7b33997ae8d Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Mon, 30 Dec 2019 22:02:17 +0600 Subject: [PATCH 05/23] Use StreamExt::for_each_concurrent --- src/dispatching/dispatchers/filter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index b76b0a3a..06feffb6 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -194,7 +194,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { Eh: ErrorHandler>, { updater - .for_each(|res| async { + .for_each_concurrent(None, |res| async { let Update { kind, id } = match res { Ok(upd) => upd, Err(err) => { From d750e1c3dc653bcf093f060e3ea616da4500a02b Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Mon, 30 Dec 2019 22:35:22 +0600 Subject: [PATCH 06/23] Fix all the errors --- src/dispatching/dispatchers/filter.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index 06feffb6..148b2256 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -19,7 +19,7 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// Filters and handlers are executed in order of registering. The pseudocode /// looks like this: /// -/// ``` +/// ```no /// for pair in handlers_and_filters { /// if pair.filter.test(update) { /// pair.handle(update); @@ -39,17 +39,15 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// /// use teloxide::{ /// dispatching::{ -/// dispatchers::filter::{ -/// error_policy::ErrorPolicy, FilterDispatcher, -/// }, -/// updater::polling, +/// dispatchers::filter::FilterDispatcher, updaters::polling_basic, /// }, /// types::Message, /// Bot, /// }; /// -/// async fn handle_edited_message(mes: Message) { -/// println!("Edited message: {:?}", mes) +/// async fn handle_edited_message(mes: Message) -> Result<(), Infallible> { +/// println!("Edited message: {:?}", mes); +/// Ok(()) /// } /// /// let bot = Bot::new("TOKEN"); @@ -59,14 +57,15 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// let mut dp = FilterDispatcher::::new(|_| async {}) /// // Add 'handler' that will handle all messages sent to the bot /// .message_handler(true, |mes: Message| async move { -/// println!("New message: {:?}", mes) +/// println!("New message: {:?}", mes); +/// Ok(()) /// }) /// // Add 'handler' that will handle all /// // messages edited in chat with the bot /// .edited_message_handler(true, handle_edited_message); /// /// // Start dispatching updates from long polling -/// dp.dispatch(polling(&bot)).await; +/// dp.dispatch(polling_basic(&bot)).await; /// # } /// ``` /// @@ -319,6 +318,7 @@ mod tests { let mut dp = FilterDispatcher::::new(|_| async {}) .message_handler(true, |_mes: Message| async move { counter.fetch_add(1, Ordering::SeqCst); + Ok::<_, Infallible>(()) }) .message_handler(true, |_mes: Message| async move { counter2.fetch_add(1, Ordering::SeqCst); From 8345936a9599f34c235eb18074c6453b7e2eed2c Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Mon, 30 Dec 2019 22:44:27 +0600 Subject: [PATCH 07/23] Improve docs --- src/dispatching/error_handler.rs | 2 +- src/dispatching/handler.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handler.rs index d57602b9..c663b054 100644 --- a/src/dispatching/error_handler.rs +++ b/src/dispatching/error_handler.rs @@ -6,7 +6,7 @@ // Infallible used here instead of `!` to be compatible with rust <1.41. use std::{convert::Infallible, future::Future, pin::Pin}; -/// A handler of an error. +/// An asynchronous handler of an error. pub trait ErrorHandler { fn handle_error<'a>( &'a self, diff --git a/src/dispatching/handler.rs b/src/dispatching/handler.rs index e919594c..fad633e2 100644 --- a/src/dispatching/handler.rs +++ b/src/dispatching/handler.rs @@ -1,6 +1,6 @@ use std::{future::Future, pin::Pin}; -/// A handler of a successful value. +/// An asynchronous handler of a value. pub trait Handler { #[must_use] fn handle<'a>( From 3c2c1636a4621b06ea0b1a9730f1033cb5b42b0f Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 00:07:54 +0600 Subject: [PATCH 08/23] Fix the docs --- src/dispatching/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching/handler.rs b/src/dispatching/handler.rs index fad633e2..36dbd337 100644 --- a/src/dispatching/handler.rs +++ b/src/dispatching/handler.rs @@ -12,7 +12,7 @@ pub trait Handler { } /// The implementation of `Handler` for `Fn(U) -> Future`. +/// E>>`. /// /// Looks quite strange for now, but with stabilised asynchronous traits it /// should be prettier. From 243b4c91731a9bf9bc9c5823edb1a96780f96eed Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 00:38:02 +0600 Subject: [PATCH 09/23] Add error_handler::Print --- src/dispatching/error_handler.rs | 37 +++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handler.rs index c663b054..82a8f344 100644 --- a/src/dispatching/error_handler.rs +++ b/src/dispatching/error_handler.rs @@ -4,7 +4,7 @@ //! should be prettier. // Infallible used here instead of `!` to be compatible with rust <1.41. -use std::{convert::Infallible, future::Future, pin::Pin}; +use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin}; /// An asynchronous handler of an error. pub trait ErrorHandler { @@ -21,7 +21,7 @@ pub trait ErrorHandler { /// ## Example /// ``` /// # #[tokio::main] -/// # async fn main_() { +/// # async fn main() { /// use teloxide::dispatching::error_handler::{ErrorHandler, Ignore}; /// /// Ignore.handle_error(()).await; @@ -50,7 +50,7 @@ impl ErrorHandler for Ignore { /// ## Examples /// ``` /// # #[tokio::main] -/// # async fn main_() { +/// # async fn main() { /// use std::convert::{Infallible, TryInto}; /// /// use teloxide::dispatching::error_handler::{ErrorHandler, IgnoreSafe}; @@ -90,6 +90,37 @@ impl ErrorHandler for IgnoreSafe { } } +/// An error handler that prints all errors passed into it. +/// +/// ## Example +/// ``` +/// # #[tokio::main] +/// # async fn main() { +/// use teloxide::dispatching::error_handler::{ErrorHandler, Print}; +/// +/// Print.handle_error(()).await; +/// Print.handle_error(404).await; +/// Print.handle_error(String::from("error")).await; +/// # } +/// ``` +pub struct Print; + +impl ErrorHandler for Print +where + E: Debug, +{ + fn handle_error<'a>( + &'a self, + error: E, + ) -> Pin + 'a>> + where + E: 'a, + { + log::debug!("error: {:?}", error); + Box::pin(async {}) + } +} + /// The implementation of `ErrorHandler` for `Fn(error) -> Future`. /// /// ## Example From fd941beec0f047423a64366d375a480b48d982af Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 00:55:12 +0600 Subject: [PATCH 10/23] Fix Clippy --- src/dispatching/error_handler.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handler.rs index 82a8f344..a8f78ca3 100644 --- a/src/dispatching/error_handler.rs +++ b/src/dispatching/error_handler.rs @@ -21,7 +21,7 @@ pub trait ErrorHandler { /// ## Example /// ``` /// # #[tokio::main] -/// # async fn main() { +/// # async fn main_() { /// use teloxide::dispatching::error_handler::{ErrorHandler, Ignore}; /// /// Ignore.handle_error(()).await; @@ -50,7 +50,7 @@ impl ErrorHandler for Ignore { /// ## Examples /// ``` /// # #[tokio::main] -/// # async fn main() { +/// # async fn main_() { /// use std::convert::{Infallible, TryInto}; /// /// use teloxide::dispatching::error_handler::{ErrorHandler, IgnoreSafe}; @@ -95,7 +95,7 @@ impl ErrorHandler for IgnoreSafe { /// ## Example /// ``` /// # #[tokio::main] -/// # async fn main() { +/// # async fn main_() { /// use teloxide::dispatching::error_handler::{ErrorHandler, Print}; /// /// Print.handle_error(()).await; From 3b4a8d1bb55e187f0d0a0e34e1e830f3f051f1fc Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 01:12:38 +0600 Subject: [PATCH 11/23] Remove the unnecessary docs --- src/dispatching/error_handler.rs | 3 --- src/dispatching/handler.rs | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handler.rs index a8f78ca3..ed3703af 100644 --- a/src/dispatching/error_handler.rs +++ b/src/dispatching/error_handler.rs @@ -1,7 +1,4 @@ //! Error handlers. -//! -//! Looks quite strange for now, but with stabilised asynchronous traits it -//! should be prettier. // Infallible used here instead of `!` to be compatible with rust <1.41. use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin}; diff --git a/src/dispatching/handler.rs b/src/dispatching/handler.rs index 36dbd337..c0911be1 100644 --- a/src/dispatching/handler.rs +++ b/src/dispatching/handler.rs @@ -13,9 +13,6 @@ pub trait Handler { /// The implementation of `Handler` for `Fn(U) -> Future>`. -/// -/// Looks quite strange for now, but with stabilised asynchronous traits it -/// should be prettier. impl Handler for F where F: Fn(T) -> Fut, From 0c5189a39ba31f921b7414b45956b0e38ef6eb10 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 01:15:58 +0600 Subject: [PATCH 12/23] Add #[must_use] to Handler --- src/dispatching/dispatchers/filter.rs | 2 +- src/dispatching/error_handler.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index 148b2256..5f8d3595 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -198,7 +198,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { Ok(upd) => upd, Err(err) => { self.error_handler - .handle_error(ErrorKind::FromUpdater(err)); + .handle_error(ErrorKind::FromUpdater(err)).await; return; } }; diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handler.rs index ed3703af..56c5a530 100644 --- a/src/dispatching/error_handler.rs +++ b/src/dispatching/error_handler.rs @@ -5,6 +5,7 @@ use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin}; /// An asynchronous handler of an error. pub trait ErrorHandler { + #[must_use] fn handle_error<'a>( &'a self, error: E, From 17f29ccb2ebba8b5a761d34fd4c2a257b696bda8 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 01:21:21 +0600 Subject: [PATCH 13/23] Fix cargo fmt --- src/dispatching/dispatchers/filter.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index 5f8d3595..d5c0b6fb 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -198,7 +198,8 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { Ok(upd) => upd, Err(err) => { self.error_handler - .handle_error(ErrorKind::FromUpdater(err)).await; + .handle_error(ErrorKind::FromUpdater(err)) + .await; return; } }; From d73eecca3640c276d108f34a9cbb521eba7fd84a Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 01:21:50 +0600 Subject: [PATCH 14/23] Rename error_handler.rs -> error_handlers.rs --- .../{error_handler.rs => error_handlers.rs} | 10 +++++----- src/dispatching/mod.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) rename src/dispatching/{error_handler.rs => error_handlers.rs} (89%) diff --git a/src/dispatching/error_handler.rs b/src/dispatching/error_handlers.rs similarity index 89% rename from src/dispatching/error_handler.rs rename to src/dispatching/error_handlers.rs index 56c5a530..1d082da7 100644 --- a/src/dispatching/error_handler.rs +++ b/src/dispatching/error_handlers.rs @@ -20,7 +20,7 @@ pub trait ErrorHandler { /// ``` /// # #[tokio::main] /// # async fn main_() { -/// use teloxide::dispatching::error_handler::{ErrorHandler, Ignore}; +/// use teloxide::dispatching::error_handlers::{ErrorHandler, Ignore}; /// /// Ignore.handle_error(()).await; /// Ignore.handle_error(404).await; @@ -51,7 +51,7 @@ impl ErrorHandler for Ignore { /// # async fn main_() { /// use std::convert::{Infallible, TryInto}; /// -/// use teloxide::dispatching::error_handler::{ErrorHandler, IgnoreSafe}; +/// use teloxide::dispatching::error_handlers::{ErrorHandler, IgnoreSafe}; /// /// let result: Result = "str".try_into(); /// match result { @@ -59,7 +59,7 @@ impl ErrorHandler for Ignore { /// Err(inf) => IgnoreSafe.handle_error(inf).await, /// } /// -/// IgnoreSafe.handle_error(return).await; // return type of `return` is `!` (aka never) +/// IgnoreSafe.handle_error(return;).await; // return type of `return` is `!` (aka never) /// # } /// ``` /// @@ -94,7 +94,7 @@ impl ErrorHandler for IgnoreSafe { /// ``` /// # #[tokio::main] /// # async fn main_() { -/// use teloxide::dispatching::error_handler::{ErrorHandler, Print}; +/// use teloxide::dispatching::error_handlers::{ErrorHandler, Print}; /// /// Print.handle_error(()).await; /// Print.handle_error(404).await; @@ -125,7 +125,7 @@ where /// ``` /// # #[tokio::main] /// # async fn main_() { -/// use teloxide::dispatching::error_handler::ErrorHandler; +/// use teloxide::dispatching::error_handlers::ErrorHandler; /// /// let mut closure = |e: i32| async move { eprintln!("Error code{}", e) }; /// diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index 5eb6e445..e36a097c 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -1,12 +1,12 @@ //! Update dispatching. pub mod dispatchers; -pub mod error_handler; +pub mod error_handlers; pub mod filters; mod handler; pub mod updaters; -pub use error_handler::ErrorHandler; +pub use error_handlers::ErrorHandler; pub use filters::Filter; pub use handler::Handler; pub use updaters::Updater; From 05f8ff13dae9704d2ee43f19e8ffe0c7ad040470 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 01:42:31 +0600 Subject: [PATCH 15/23] Fix error_handlers.rs --- src/dispatching/error_handlers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching/error_handlers.rs b/src/dispatching/error_handlers.rs index 1d082da7..acf8abf9 100644 --- a/src/dispatching/error_handlers.rs +++ b/src/dispatching/error_handlers.rs @@ -59,7 +59,7 @@ impl ErrorHandler for Ignore { /// Err(inf) => IgnoreSafe.handle_error(inf).await, /// } /// -/// IgnoreSafe.handle_error(return;).await; // return type of `return` is `!` (aka never) +/// IgnoreSafe.handle_error(return).await; // return type of `return` is `!` (aka never) /// # } /// ``` /// From b8579c52d1641d9a3e8edb8c9913ad539c93853e Mon Sep 17 00:00:00 2001 From: Waffle Date: Mon, 30 Dec 2019 23:15:52 +0300 Subject: [PATCH 16/23] Clean `polling` for a bit --- src/dispatching/updaters.rs | 70 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/dispatching/updaters.rs b/src/dispatching/updaters.rs index 3ee17ec9..750a04b1 100644 --- a/src/dispatching/updaters.rs +++ b/src/dispatching/updaters.rs @@ -115,12 +115,9 @@ impl Updater for S where S: Stream> {} /// Returns a long polling updater with the default configuration. /// -/// It is the same as calling [`polling_advanced`] with `timeout` of 30 seconds, -/// `limit=100` and receive all kinds of updates. -/// -/// [`polling_advanced`]: polling_advanced -pub fn polling_basic(bot: &Bot) -> impl Updater + '_ { - polling_advanced::<&[_]>(bot, Duration::from_secs(30), 100, &[]) +/// [`polling`]: polling +pub fn polling_default(bot: &Bot) -> impl Updater + '_ { + polling(bot, None, None, None) } /// Returns a long/short polling updater with some additional options. @@ -130,38 +127,41 @@ pub fn polling_basic(bot: &Bot) -> impl Updater + '_ { /// - `limit`: Limits the number of updates to be retrieved at once. Values /// between 1—100 are accepted. /// - `allowed_updates`: A list the types of updates you want to receive. -pub fn polling_advanced<'a, A>( - bot: &'a Bot, - timeout: Duration, - limit: u8, - allowed_updates: A, -) -> impl Updater + 'a -where - A: Into<&'a [AllowedUpdate]>, -{ - let mut allowed_updates = Some(allowed_updates.into()); +/// See [`GetUpdates`] for defaults. +/// +/// See also: [`polling_default`](polling_default) +/// +/// [`GetUpdates`]: crate::requests::payloads::GetUpdates +pub fn polling( + bot: &Bot, + timeout: Option, + limit: Option, + allowed_updates: Option>, +) -> impl Updater + '_ { + let timeout = + timeout.map(|t| t.as_secs().try_into().expect("timeout is too big")); - stream::unfold((bot, 0), move |(bot, mut offset)| async move { - let updates = match bot - .get_updates() - .offset(offset) - .timeout(timeout.as_secs().try_into().expect("timeout is too big")) - .limit(limit) - .allowed_updates(allowed_updates.take().unwrap_or(&[])) - .send() - .await - { - Err(err) => vec![Err(err)], - Ok(updates) => { - if let Some(upd) = updates.last() { - offset = upd.id + 1; + stream::unfold( + (allowed_updates, bot, 0), + move |(mut allowed_updates, bot, mut offset)| async move { + let mut req = bot.get_updates().offset(offset); + req.payload.timeout = timeout; + req.payload.limit = limit; + req.payload.allowed_updates = allowed_updates.take(); + + let updates = match req.send().await { + Err(err) => vec![Err(err)], + Ok(updates) => { + if let Some(upd) = updates.last() { + offset = upd.id + 1; + } + updates.into_iter().map(Ok).collect::>() } - updates.into_iter().map(Ok).collect::>() - } - }; + }; - Some((stream::iter(updates), (bot, offset))) - }) + Some((stream::iter(updates), (allowed_updates, bot, offset))) + }, + ) .flatten() } From 49771e2d89e2950febff8e35fc0481e7b75e4109 Mon Sep 17 00:00:00 2001 From: Waffle Date: Mon, 30 Dec 2019 23:26:07 +0300 Subject: [PATCH 17/23] Fix doc test that uses polling --- src/dispatching/dispatchers/filter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index d5c0b6fb..a6fa842b 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -39,7 +39,7 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// /// use teloxide::{ /// dispatching::{ -/// dispatchers::filter::FilterDispatcher, updaters::polling_basic, +/// dispatchers::filter::FilterDispatcher, updaters::polling_default, /// }, /// types::Message, /// Bot, @@ -65,7 +65,7 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// .edited_message_handler(true, handle_edited_message); /// /// // Start dispatching updates from long polling -/// dp.dispatch(polling_basic(&bot)).await; +/// dp.dispatch(polling_default(&bot)).await; /// # } /// ``` /// From be43bd6cdcfdc48ccb4bf9a362e07b591d0c4906 Mon Sep 17 00:00:00 2001 From: Waffle Date: Mon, 30 Dec 2019 23:35:58 +0300 Subject: [PATCH 18/23] fix doc --- src/dispatching/updaters.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dispatching/updaters.rs b/src/dispatching/updaters.rs index 750a04b1..6068fb55 100644 --- a/src/dispatching/updaters.rs +++ b/src/dispatching/updaters.rs @@ -2,8 +2,8 @@ //! //! The key trait here is [`Updater`]. You can get it by these functions: //! -//! - [`polling_basic`], which returns a default long polling updater. -//! - [`polling_advanced`], which returns a long/short polling updater with +//! - [`polling_default`], which returns a default long polling updater. +//! - [`polling`], which returns a long/short polling updater with //! your configuration. //! //! And then you can pass it directly to a dispatcher. @@ -91,8 +91,8 @@ //! updates `0..=N`. //! //! [`Updater`]: Updater -//! [`polling_basic`]: polling_basic -//! [`polling_advanced`]: polling_advanced +//! [`polling_default`]: polling_default +//! [`polling`]: polling //! [`Dispatcher`]: crate::dispatching::Dispatcher::dispatch //! [`Box::get_updates`]: crate::Bot::get_updates //! [getting updates]: https://core.telegram.org/bots/api#getting-updates From 9ee9c24024c95b0f5e671efa7e55649264d0f722 Mon Sep 17 00:00:00 2001 From: Waffle Date: Mon, 30 Dec 2019 23:41:10 +0300 Subject: [PATCH 19/23] fmt :facepalm: --- src/dispatching/updaters.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dispatching/updaters.rs b/src/dispatching/updaters.rs index 6068fb55..0c5248bb 100644 --- a/src/dispatching/updaters.rs +++ b/src/dispatching/updaters.rs @@ -3,8 +3,8 @@ //! The key trait here is [`Updater`]. You can get it by these functions: //! //! - [`polling_default`], which returns a default long polling updater. -//! - [`polling`], which returns a long/short polling updater with -//! your configuration. +//! - [`polling`], which returns a long/short polling updater with your +//! configuration. //! //! And then you can pass it directly to a dispatcher. //! From 43b417b9f3899650ed64ebebc589babadc05f476 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 14:12:18 +0600 Subject: [PATCH 20/23] Delete dispatching::dispatchers --- src/dispatching/dispatchers/mod.rs | 5 ----- src/dispatching/{dispatchers/filter.rs => filter_dp.rs} | 4 ++-- src/dispatching/mod.rs | 3 ++- 3 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 src/dispatching/dispatchers/mod.rs rename src/dispatching/{dispatchers/filter.rs => filter_dp.rs} (98%) diff --git a/src/dispatching/dispatchers/mod.rs b/src/dispatching/dispatchers/mod.rs deleted file mode 100644 index f3a34672..00000000 --- a/src/dispatching/dispatchers/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Different types of dispatchers. - -pub use filter::FilterDispatcher; - -pub mod filter; diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/filter_dp.rs similarity index 98% rename from src/dispatching/dispatchers/filter.rs rename to src/dispatching/filter_dp.rs index d5c0b6fb..05da6f27 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/filter_dp.rs @@ -39,7 +39,7 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// /// use teloxide::{ /// dispatching::{ -/// dispatchers::filter::FilterDispatcher, updaters::polling_basic, +/// dispatchers::filter_dp::FilterDispatcher, updaters::polling_basic, /// }, /// types::Message, /// Bot, @@ -304,7 +304,7 @@ mod tests { }; use crate::{ - dispatching::{dispatchers::filter::FilterDispatcher, Updater}, + dispatching::{dispatchers::filter_dp::FilterDispatcher, Updater}, types::{ Chat, ChatKind, ForwardKind, MediaKind, Message, MessageKind, Sender, Update, UpdateKind, User, diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index e36a097c..d92740ae 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -1,6 +1,6 @@ //! Update dispatching. -pub mod dispatchers; +mod filter_dp; pub mod error_handlers; pub mod filters; mod handler; @@ -10,3 +10,4 @@ pub use error_handlers::ErrorHandler; pub use filters::Filter; pub use handler::Handler; pub use updaters::Updater; +pub use filter_dp::FilterDispatcher; \ No newline at end of file From 52f1b6d8a9527d84ac3460db4d3e517b924b8f81 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 14:22:15 +0600 Subject: [PATCH 21/23] Use Either instead of ErrorKind --- Cargo.toml | 1 + src/dispatching/filter_dp.rs | 54 ++++++++++++++---------------------- src/dispatching/mod.rs | 4 +-- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 472bdb86..abc07eaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ futures-preview = "0.3.0-alpha.19" async-trait = "0.1.13" thiserror = "1.0.2" serde_with_macros = "1.0.1" +either = "1.5.3" [features] default = [] diff --git a/src/dispatching/filter_dp.rs b/src/dispatching/filter_dp.rs index 05da6f27..78aab5a2 100644 --- a/src/dispatching/filter_dp.rs +++ b/src/dispatching/filter_dp.rs @@ -9,6 +9,7 @@ use crate::{ UpdateKind, }, }; +use either::Either; type FilterWithHandler<'a, T, E> = (Box + 'a>, Box + 'a>); @@ -38,9 +39,7 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// use std::convert::Infallible; /// /// use teloxide::{ -/// dispatching::{ -/// dispatchers::filter_dp::FilterDispatcher, updaters::polling_basic, -/// }, +/// dispatching::{updaters::polling_basic, FilterDispatcher}, /// types::Message, /// Bot, /// }; @@ -83,19 +82,10 @@ pub struct FilterDispatcher<'a, E, Eh> { error_handler: Eh, } -/// An error produced either from [`Updater`] or [`Handler`]. -/// -/// [`Updater`]: crate::dispatching::Updater -/// [`Handler`]: crate::dispatching::Handler -pub enum ErrorKind { - FromUpdater(E1), - FromHandler(E2), -} - -impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { - pub fn new(error_handler: Eh) -> Self +impl<'a, HandlerE, Eh> FilterDispatcher<'a, HandlerE, Eh> { + pub fn new(error_handler: Eh) -> Self where - Eh: ErrorHandler>, + Eh: ErrorHandler>, { FilterDispatcher { message_handlers: Vec::new(), @@ -112,7 +102,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { pub fn message_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler + 'a, + H: Handler + 'a, { self.message_handlers .push((Box::new(filter), Box::new(handler))); @@ -122,7 +112,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { pub fn edited_message_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler + 'a, + H: Handler + 'a, { self.edited_message_handlers .push((Box::new(filter), Box::new(handler))); @@ -132,7 +122,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { pub fn channel_post_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler + 'a, + H: Handler + 'a, { self.channel_post_handlers .push((Box::new(filter), Box::new(handler))); @@ -146,7 +136,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { ) -> Self where F: Filter + 'a, - H: Handler + 'a, + H: Handler + 'a, { self.edited_channel_post_handlers .push((Box::new(filter), Box::new(handler))); @@ -156,7 +146,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { pub fn inline_query_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler + 'a, + H: Handler + 'a, { self.inline_query_handlers .push((Box::new(filter), Box::new(handler))); @@ -170,7 +160,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { ) -> Self where F: Filter + 'a, - H: Handler + 'a, + H: Handler + 'a, { self.chosen_inline_result_handlers .push((Box::new(filter), Box::new(handler))); @@ -180,17 +170,17 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { pub fn callback_query_handler(mut self, filter: F, handler: H) -> Self where F: Filter + 'a, - H: Handler + 'a, + H: Handler + 'a, { self.callback_query_handlers .push((Box::new(filter), Box::new(handler))); self } - pub async fn dispatch(&mut self, updater: U) + pub async fn dispatch(&mut self, updater: U) where - U: Updater + 'a, - Eh: ErrorHandler>, + U: Updater + 'a, + Eh: ErrorHandler>, { updater .for_each_concurrent(None, |res| async { @@ -198,7 +188,7 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { Ok(upd) => upd, Err(err) => { self.error_handler - .handle_error(ErrorKind::FromUpdater(err)) + .handle_error(Either::Left(err)) .await; return; } @@ -272,20 +262,18 @@ impl<'a, E2, Eh> FilterDispatcher<'a, E2, Eh> { .await; } - async fn handle( + async fn handle( update: T, - handlers: &FiltersWithHandlers<'a, T, E2>, + handlers: &FiltersWithHandlers<'a, T, HandlerE>, error_handler: &Eh, ) where T: std::fmt::Debug, - Eh: ErrorHandler>, + Eh: ErrorHandler>, { for x in handlers { if x.0.test(&update) { if let Err(err) = x.1.handle(update).await { - error_handler - .handle_error(ErrorKind::FromHandler(err)) - .await + error_handler.handle_error(Either::Right(err)).await } return; @@ -304,7 +292,7 @@ mod tests { }; use crate::{ - dispatching::{dispatchers::filter_dp::FilterDispatcher, Updater}, + dispatching::{FilterDispatcher, Updater}, types::{ Chat, ChatKind, ForwardKind, MediaKind, Message, MessageKind, Sender, Update, UpdateKind, User, diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index d92740ae..f738757d 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -1,13 +1,13 @@ //! Update dispatching. -mod filter_dp; pub mod error_handlers; +mod filter_dp; pub mod filters; mod handler; pub mod updaters; pub use error_handlers::ErrorHandler; +pub use filter_dp::FilterDispatcher; pub use filters::Filter; pub use handler::Handler; pub use updaters::Updater; -pub use filter_dp::FilterDispatcher; \ No newline at end of file From b97dbe0cb5edda00ac8410fd299cd904541a0709 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 15:36:45 +0600 Subject: [PATCH 22/23] Add a flowchart to FilterDispatcher --- media/FILTER_DP_FLOWCHART.png | Bin 0 -> 35540 bytes src/dispatching/filter_dp.rs | 23 ++++++++++++----------- src/dispatching/updaters.rs | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 media/FILTER_DP_FLOWCHART.png diff --git a/media/FILTER_DP_FLOWCHART.png b/media/FILTER_DP_FLOWCHART.png new file mode 100644 index 0000000000000000000000000000000000000000..53641075e664d3c2a157ebaf48df228150124956 GIT binary patch literal 35540 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 literal 0 HcmV?d00001 diff --git a/src/dispatching/filter_dp.rs b/src/dispatching/filter_dp.rs index d6009f1a..1a62db2c 100644 --- a/src/dispatching/filter_dp.rs +++ b/src/dispatching/filter_dp.rs @@ -17,19 +17,19 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// A dispatcher based on filters. /// -/// Filters and handlers are executed in order of registering. The pseudocode -/// looks like this: +/// It consists of: +/// 1. [`ErrorHandler`] than handles errors both from [`Updater`] and +/// [`Handler`]. +/// 2. Filters and handlers. /// -/// ```no -/// for pair in handlers_and_filters { -/// if pair.filter.test(update) { -/// pair.handle(update); -/// return; -/// } -/// } +/// First you register filters and handlers using the methods defined below, and +/// then you call [`.dispatch(updater)`]. Filters and handlers are executed in +/// order of registering. The following flowchart represents how this dispatcher +/// acts: /// -/// log("unhandeled update: " + update); -/// ``` +///

+/// +///
/// /// ## Examples /// @@ -70,6 +70,7 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// /// [`std::fmt::Debug`]: std::fmt::Debug /// [updater]: crate::dispatching::updater +/// [`.dispatch(updater)`]: FilterDispatcher::dispatch pub struct FilterDispatcher<'a, E, Eh> { message_handlers: FiltersWithHandlers<'a, Message, E>, edited_message_handlers: FiltersWithHandlers<'a, Message, E>, diff --git a/src/dispatching/updaters.rs b/src/dispatching/updaters.rs index 0c5248bb..f7fed6f5 100644 --- a/src/dispatching/updaters.rs +++ b/src/dispatching/updaters.rs @@ -115,7 +115,7 @@ impl Updater for S where S: Stream> {} /// Returns a long polling updater with the default configuration. /// -/// [`polling`]: polling +/// See also: [`polling`](polling). pub fn polling_default(bot: &Bot) -> impl Updater + '_ { polling(bot, None, None, None) } @@ -129,7 +129,7 @@ pub fn polling_default(bot: &Bot) -> impl Updater + '_ { /// - `allowed_updates`: A list the types of updates you want to receive. /// See [`GetUpdates`] for defaults. /// -/// See also: [`polling_default`](polling_default) +/// See also: [`polling_default`](polling_default). /// /// [`GetUpdates`]: crate::requests::payloads::GetUpdates pub fn polling( From b0f85dea5b821257f6294f3bfc65cf69d3336ab7 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Tue, 31 Dec 2019 17:16:01 +0600 Subject: [PATCH 23/23] Fix grammatical mistakes in the docs --- src/dispatching/filter_dp.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dispatching/filter_dp.rs b/src/dispatching/filter_dp.rs index 1a62db2c..f7d38bc8 100644 --- a/src/dispatching/filter_dp.rs +++ b/src/dispatching/filter_dp.rs @@ -33,7 +33,7 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// /// ## Examples /// -/// Simplest example: +/// The simplest example: /// ```no_run /// # async fn run() { /// use std::convert::Infallible; @@ -51,19 +51,19 @@ type FiltersWithHandlers<'a, T, E> = Vec>; /// /// let bot = Bot::new("TOKEN"); /// -/// // create dispatching which handlers can't fail -/// // with error policy that just ignores all errors (that can't ever happen) +/// // Create a dispatcher which handlers can't fail with the +/// // error handler that just ignores all errors (that can't ever happen). /// let mut dp = FilterDispatcher::::new(|_| async {}) -/// // Add 'handler' that will handle all messages sent to the bot +/// // Add a handler, which handles all messages sent to the bot. /// .message_handler(true, |mes: Message| async move { /// println!("New message: {:?}", mes); /// Ok(()) /// }) -/// // Add 'handler' that will handle all -/// // messages edited in chat with the bot +/// // Add a handler, which handles all messages edited in a chat +/// // with the bot. /// .edited_message_handler(true, handle_edited_message); /// -/// // Start dispatching updates from long polling +/// // Start dispatching updates using long polling. /// dp.dispatch(polling_default(&bot)).await; /// # } /// ```