mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-03 09:49:07 +01:00
212 lines
5.3 KiB
Rust
212 lines
5.3 KiB
Rust
//! Convenient error handling.
|
|
|
|
use futures::future::BoxFuture;
|
|
use std::{convert::Infallible, fmt::Debug, future::Future, sync::Arc};
|
|
|
|
/// An asynchronous handler of an error.
|
|
///
|
|
/// See [the module-level documentation for the design
|
|
/// overview](crate::dispatching).
|
|
pub trait ErrorHandler<E> {
|
|
#[must_use]
|
|
fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()>;
|
|
}
|
|
|
|
impl<E, F, Fut> ErrorHandler<E> for F
|
|
where
|
|
F: Fn(E) -> Fut + Send + Sync + 'static,
|
|
E: Send + 'static,
|
|
Fut: Future<Output = ()> + Send,
|
|
{
|
|
fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()> {
|
|
Box::pin(async move { self(error).await })
|
|
}
|
|
}
|
|
|
|
/// Something that can be handled by an error handler.
|
|
///
|
|
/// ## Examples
|
|
/// ```
|
|
/// use teloxide::error_handlers::OnError;
|
|
///
|
|
/// # #[tokio::main]
|
|
/// # async fn main_() {
|
|
/// // Prints nothing
|
|
/// let ok: Result<i32, i32> = Ok(200);
|
|
/// ok.log_on_error().await;
|
|
///
|
|
/// // Prints "Error: 404"
|
|
/// let err: Result<i32, i32> = Err(404);
|
|
/// err.log_on_error().await;
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// Use an arbitrary error handler:
|
|
/// ```
|
|
/// use teloxide::error_handlers::{IgnoringErrorHandler, OnError};
|
|
///
|
|
/// # #[tokio::main]
|
|
/// # async fn main_() {
|
|
/// let err: Result<i32, i32> = Err(404);
|
|
/// err.on_error(IgnoringErrorHandler::new()).await;
|
|
/// # }
|
|
/// ```
|
|
pub trait OnError<E> {
|
|
#[must_use]
|
|
fn on_error<'a, Eh>(self, eh: Arc<Eh>) -> BoxFuture<'a, ()>
|
|
where
|
|
Self: 'a,
|
|
Eh: ErrorHandler<E> + Send + Sync,
|
|
Arc<Eh>: 'a;
|
|
|
|
/// A shortcut for `.on_error(LoggingErrorHandler::new())`.
|
|
#[must_use]
|
|
fn log_on_error<'a>(self) -> BoxFuture<'a, ()>
|
|
where
|
|
Self: Sized + 'a,
|
|
E: Debug,
|
|
{
|
|
self.on_error(LoggingErrorHandler::new())
|
|
}
|
|
}
|
|
|
|
impl<T, E> OnError<E> for Result<T, E>
|
|
where
|
|
T: Send,
|
|
E: Send,
|
|
{
|
|
fn on_error<'a, Eh>(self, eh: Arc<Eh>) -> BoxFuture<'a, ()>
|
|
where
|
|
Self: 'a,
|
|
Eh: ErrorHandler<E> + Send + Sync,
|
|
Arc<Eh>: 'a,
|
|
{
|
|
Box::pin(async move {
|
|
if let Err(error) = self {
|
|
eh.handle_error(error).await;
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/// A handler that silently ignores all errors.
|
|
///
|
|
/// ## Example
|
|
/// ```
|
|
/// # #[tokio::main]
|
|
/// # async fn main_() {
|
|
/// use teloxide::error_handlers::{ErrorHandler, IgnoringErrorHandler};
|
|
///
|
|
/// IgnoringErrorHandler::new().handle_error(()).await;
|
|
/// IgnoringErrorHandler::new().handle_error(404).await;
|
|
/// IgnoringErrorHandler::new().handle_error("error").await;
|
|
/// # }
|
|
/// ```
|
|
#[derive(Clone, Copy)]
|
|
pub struct IgnoringErrorHandler;
|
|
|
|
impl IgnoringErrorHandler {
|
|
#[must_use]
|
|
pub fn new() -> Arc<Self> {
|
|
Arc::new(Self)
|
|
}
|
|
}
|
|
|
|
impl<E> ErrorHandler<E> for IgnoringErrorHandler {
|
|
fn handle_error(self: Arc<Self>, _: E) -> BoxFuture<'static, ()> {
|
|
Box::pin(async {})
|
|
}
|
|
}
|
|
|
|
/// A 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::error_handlers::{ErrorHandler, IgnoringErrorHandlerSafe};
|
|
///
|
|
/// let result: Result<String, Infallible> = "str".try_into();
|
|
/// match result {
|
|
/// Ok(string) => println!("{}", string),
|
|
/// Err(inf) => IgnoringErrorHandlerSafe::new().handle_error(inf).await,
|
|
/// }
|
|
///
|
|
/// IgnoringErrorHandlerSafe::new().handle_error(return).await; // return type of `return` is `!` (aka never)
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// ```compile_fail
|
|
/// use teloxide::dispatching::{ErrorHandler, IgnoringErrorHandlerSafe};
|
|
///
|
|
/// IgnoringErrorHandlerSafe.handle_error(0);
|
|
/// ```
|
|
///
|
|
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
|
|
/// [`Infallible`]: std::convert::Infallible
|
|
#[derive(Clone, Copy)]
|
|
pub struct IgnoringErrorHandlerSafe;
|
|
|
|
impl IgnoringErrorHandlerSafe {
|
|
#[must_use]
|
|
pub fn new() -> Arc<Self> {
|
|
Arc::new(Self)
|
|
}
|
|
}
|
|
|
|
#[allow(unreachable_code)]
|
|
impl ErrorHandler<Infallible> for IgnoringErrorHandlerSafe {
|
|
fn handle_error(self: Arc<Self>, _: Infallible) -> BoxFuture<'static, ()> {
|
|
Box::pin(async {})
|
|
}
|
|
}
|
|
|
|
/// A handler that log all errors passed into it.
|
|
///
|
|
/// ## Example
|
|
/// ```
|
|
/// # #[tokio::main]
|
|
/// # async fn main_() {
|
|
/// use teloxide::error_handlers::{ErrorHandler, LoggingErrorHandler};
|
|
///
|
|
/// LoggingErrorHandler::new().handle_error(()).await;
|
|
/// LoggingErrorHandler::with_custom_text("Omg1").handle_error(404).await;
|
|
/// LoggingErrorHandler::with_custom_text("Omg2").handle_error("Invalid data type!").await;
|
|
/// # }
|
|
/// ```
|
|
pub struct LoggingErrorHandler {
|
|
text: String,
|
|
}
|
|
|
|
impl LoggingErrorHandler {
|
|
/// Creates `LoggingErrorHandler` with a meta text before a log.
|
|
///
|
|
/// The logs will be printed in this format: `{text}: {:?}`.
|
|
#[must_use]
|
|
pub fn with_custom_text<T>(text: T) -> Arc<Self>
|
|
where
|
|
T: Into<String>,
|
|
{
|
|
Arc::new(Self { text: text.into() })
|
|
}
|
|
|
|
/// A shortcut for
|
|
/// `LoggingErrorHandler::with_custom_text("Error".to_owned())`.
|
|
#[must_use]
|
|
pub fn new() -> Arc<Self> {
|
|
Self::with_custom_text("Error".to_owned())
|
|
}
|
|
}
|
|
|
|
impl<E> ErrorHandler<E> for LoggingErrorHandler
|
|
where
|
|
E: Debug,
|
|
{
|
|
fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()> {
|
|
log::error!("{text}: {:?}", error, text = self.text);
|
|
Box::pin(async {})
|
|
}
|
|
}
|