Merge pull request #78 from telebofr/error_policy

Error policy
This commit is contained in:
Temirkhan Myrzamadi 2019-11-02 14:33:07 +00:00 committed by GitHub
commit ce2e949cb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 152 additions and 44 deletions

View file

@ -22,3 +22,4 @@ thiserror = "1.0.2"
default = []
unstable-stream = [] # add streams to public API
never-type = [] # add never type (`!`) to punlic API

View file

@ -1,32 +1,138 @@
use std::{fmt::Debug, future::Future, pin::Pin};
use std::{
pin::Pin,
future::Future,
convert::Infallible,
};
// TODO: shouldn't it be trait?
pub enum ErrorPolicy<'a, E> {
Ignore,
Log,
Custom(Box<dyn Fn(E) -> Pin<Box<dyn Future<Output = ()> + 'a>>>),
}
use async_trait::async_trait;
impl<'a, E> ErrorPolicy<'a, E>
where
E: Debug,
{
pub async fn handle_error(&self, error: E) {
match self {
Self::Ignore => {}
Self::Log => {
// TODO: better message
log::error!("Error in handler: {:?}", error)
}
Self::Custom(func) => func(error).await,
}
}
pub fn custom<F, Fut>(f: F) -> Self
/// Implementors of this trait are treated as error-handlers.
#[async_trait]
pub trait ErrorPolicy<E> {
async fn handle_error(&self, error: E)
where
F: Fn(E) -> Fut + 'static,
Fut: Future<Output = ()> + 'a,
E: 'async_trait;
}
/// Error policy that silently ignores all errors
///
/// ## Example
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use telebofr::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<E> ErrorPolicy<E> 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 telebofr::dispatching::dispatchers::filter::error_policy::{
/// ErrorPolicy,
/// IgnoreSafe,
/// };
///
/// let result: Result<String, Infallible> = "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 telebofr::dispatching::dispatchers::filter::error_policy::{
/// ErrorPolicy,
/// IgnoreSafe,
/// };
///
/// IgnoreSafe.handle_error(0);
/// ```
///
/// ## Note
/// Never type is not stabilized yet (see [`#35121`]) so all API that uses [`!`]
/// (including `impl ErrorPolicy<!> for IgnoreSafe`) we hide under the
/// `never-type` cargo feature.
///
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
/// [`Infallible`]: std::convert::Infallible
/// [`#35121`]: https://github.com/rust-lang/rust/issues/35121
pub struct IgnoreSafe;
#[cfg(feature = "never-type")]
#[async_trait]
impl ErrorPolicy<!> for IgnoreSafe {
async fn handle_error(&self, never: !)
where
!: 'async_trait
{
Self::Custom(Box::new(move |e| Box::pin(f(e))))
never
}
}
#[async_trait]
impl ErrorPolicy<Infallible> for IgnoreSafe {
async fn handle_error(&self, inf: Infallible)
where
Infallible: 'async_trait
{
match inf {}
}
}
/// Implementation of `ErrorPolicy` for `async fn`s
///
/// ## Example
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use telebofr::dispatching::dispatchers::filter::error_policy::ErrorPolicy;
///
/// let closure = |e: i32| async move {
/// eprintln!("Error code{}", e)
/// };
///
/// closure.handle_error(404).await;
/// # }
/// ```
impl<E, F, Fut> ErrorPolicy<E> for F
where
F: Fn(E) -> Fut + Sync,
Fut: Future<Output = ()> + Send,
E: Send,
{
fn handle_error<'s, 'async_trait>(&'s self, error: E) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where
's: 'async_trait,
Self: 'async_trait,
E: 'async_trait
{
Box::pin(async move { self(error).await })
}
}

View file

@ -32,11 +32,12 @@ type Handlers<'a, T, E> =
///
/// Simplest example:
/// ```no_run
/// # use telebofr::Bot;
/// use telebofr::types::Message;
/// async fn run() {
/// # async fn run() {
/// use std::convert::Infallible;
///
/// use telebofr::{
/// Bot,
/// types::Message,
/// dispatching::{
/// dispatchers::filter::{error_policy::ErrorPolicy, FilterDispatcher},
/// updater::polling,
@ -51,7 +52,7 @@ type Handlers<'a, T, E> =
///
/// // create dispatching which handlers can't fail
/// // with error policy that just ignores all errors (that can't ever happen)
/// let mut dp = FilterDispatcher::<Infallible>::new(ErrorPolicy::Ignore)
/// let mut dp = FilterDispatcher::<Infallible, _>::new(|_| async { () })
/// // Add 'handler' that will handle all messages sent to the bot
/// .message_handler(true, |mes: Message| {
/// async move { println!("New message: {:?}", mes) }
@ -66,10 +67,9 @@ type Handlers<'a, T, E> =
/// ```
///
/// [`std::fmt::Debug`]: std::fmt::Debug
/// [Custom error policy]:
/// crate::dispatching::filter::error_policy::ErrorPolicy::Custom [updater]:
/// crate::dispatching::updater
pub struct FilterDispatcher<'a, E> {
/// [Custom error policy]: crate::dispatching::filter::error_policy::ErrorPolicy::Custom
/// [updater]: crate::dispatching::updater
pub struct FilterDispatcher<'a, E, Ep> {
message_handlers: Handlers<'a, Message, E>,
edited_message_handlers: Handlers<'a, Message, E>,
channel_post_handlers: Handlers<'a, Message, E>,
@ -77,14 +77,15 @@ pub struct FilterDispatcher<'a, E> {
inline_query_handlers: Handlers<'a, (), E>,
chosen_inline_result_handlers: Handlers<'a, ChosenInlineResult, E>,
callback_query_handlers: Handlers<'a, CallbackQuery, E>,
error_policy: ErrorPolicy<'a, E>,
error_policy: Ep,
}
impl<'a, E> FilterDispatcher<'a, E>
impl<'a, E, Ep> FilterDispatcher<'a, E, Ep>
where
Ep: ErrorPolicy<E>,
E: std::fmt::Debug, // TODO: Is this really necessary?
{
pub fn new(error_policy: ErrorPolicy<'a, E>) -> Self {
pub fn new(error_policy: Ep) -> Self {
FilterDispatcher {
message_handlers: Vec::new(),
edited_message_handlers: Vec::new(),
@ -183,7 +184,6 @@ where
updates
.for_each(|res| {
async {
let res = res;
let Update { kind, id } = match res {
Ok(upd) => upd,
_ => return, // TODO: proper error handling
@ -265,10 +265,11 @@ where
}
#[async_trait(? Send)]
impl<'a, U, E> Dispatcher<'a, U> for FilterDispatcher<'a, E>
impl<'a, U, E, Ep> Dispatcher<'a, U> for FilterDispatcher<'a, E, Ep>
where
E: std::fmt::Debug,
U: Updater + 'a,
Ep: ErrorPolicy<E>,
{
async fn dispatch(&'a mut self, updater: U) {
FilterDispatcher::dispatch(self, updater).await
@ -286,9 +287,7 @@ mod tests {
use crate::{
dispatching::{
dispatchers::filter::{
error_policy::ErrorPolicy, FilterDispatcher,
},
dispatchers::filter::FilterDispatcher,
updater::StreamUpdater,
},
types::{
@ -302,7 +301,7 @@ mod tests {
let counter = &AtomicI32::new(0);
let counter2 = &AtomicI32::new(0);
let mut dp = FilterDispatcher::<Infallible>::new(ErrorPolicy::Ignore)
let mut dp = FilterDispatcher::<Infallible, _>::new(|_| async { () } )
.message_handler(true, |_mes: Message| {
async move {
counter.fetch_add(1, Ordering::SeqCst);

View file

@ -1,3 +1,5 @@
#![cfg_attr(feature = "never-type", feature(never_type))]
#[macro_use]
extern crate derive_more;
#[macro_use]