teloxide/
error_handlers.rs

1//! Convenient error handling.
2
3use futures::future::BoxFuture;
4use std::{convert::Infallible, fmt::Debug, future::Future, sync::Arc};
5
6/// An asynchronous handler of an error.
7///
8/// See [the module-level documentation for the design
9/// overview](crate::dispatching).
10pub trait ErrorHandler<E> {
11    #[must_use]
12    fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()>;
13}
14
15impl<E, F, Fut> ErrorHandler<E> for F
16where
17    F: Fn(E) -> Fut + Send + Sync + 'static,
18    E: Send + 'static,
19    Fut: Future<Output = ()> + Send,
20{
21    fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()> {
22        Box::pin(async move { self(error).await })
23    }
24}
25
26/// Something that can be handled by an error handler.
27///
28/// ## Examples
29/// ```
30/// use teloxide::error_handlers::OnError;
31///
32/// # #[tokio::main]
33/// # async fn main_() {
34/// // Prints nothing
35/// let ok: Result<i32, i32> = Ok(200);
36/// ok.log_on_error().await;
37///
38/// // Prints "Error: 404"
39/// let err: Result<i32, i32> = Err(404);
40/// err.log_on_error().await;
41/// # }
42/// ```
43///
44/// Use an arbitrary error handler:
45/// ```
46/// use teloxide::error_handlers::{IgnoringErrorHandler, OnError};
47///
48/// # #[tokio::main]
49/// # async fn main_() {
50/// let err: Result<i32, i32> = Err(404);
51/// err.on_error(IgnoringErrorHandler::new()).await;
52/// # }
53/// ```
54pub trait OnError<E> {
55    #[must_use]
56    fn on_error<'a, Eh>(self, eh: Arc<Eh>) -> BoxFuture<'a, ()>
57    where
58        Self: 'a,
59        Eh: ErrorHandler<E> + Send + Sync,
60        Arc<Eh>: 'a;
61
62    /// A shortcut for `.on_error(LoggingErrorHandler::new())`.
63    #[must_use]
64    fn log_on_error<'a>(self) -> BoxFuture<'a, ()>
65    where
66        Self: Sized + 'a,
67        E: Debug,
68    {
69        self.on_error(LoggingErrorHandler::new())
70    }
71}
72
73impl<T, E> OnError<E> for Result<T, E>
74where
75    T: Send,
76    E: Send,
77{
78    fn on_error<'a, Eh>(self, eh: Arc<Eh>) -> BoxFuture<'a, ()>
79    where
80        Self: 'a,
81        Eh: ErrorHandler<E> + Send + Sync,
82        Arc<Eh>: 'a,
83    {
84        Box::pin(async move {
85            if let Err(error) = self {
86                eh.handle_error(error).await;
87            }
88        })
89    }
90}
91
92/// A handler that silently ignores all errors.
93///
94/// ## Example
95/// ```
96/// # #[tokio::main]
97/// # async fn main_() {
98/// use teloxide::error_handlers::{ErrorHandler, IgnoringErrorHandler};
99///
100/// IgnoringErrorHandler::new().handle_error(()).await;
101/// IgnoringErrorHandler::new().handle_error(404).await;
102/// IgnoringErrorHandler::new().handle_error("error").await;
103/// # }
104/// ```
105#[derive(Clone, Copy)]
106pub struct IgnoringErrorHandler;
107
108impl IgnoringErrorHandler {
109    #[must_use]
110    pub fn new() -> Arc<Self> {
111        Arc::new(Self)
112    }
113}
114
115impl<E> ErrorHandler<E> for IgnoringErrorHandler {
116    fn handle_error(self: Arc<Self>, _: E) -> BoxFuture<'static, ()> {
117        Box::pin(async {})
118    }
119}
120
121/// A handler that silently ignores all errors that can never happen (e.g.:
122/// [`!`] or [`Infallible`]).
123///
124/// ## Examples
125/// ```
126/// # #[tokio::main]
127/// # async fn main_() {
128/// use std::convert::{Infallible, TryInto};
129///
130/// use teloxide::error_handlers::{ErrorHandler, IgnoringErrorHandlerSafe};
131///
132/// let result: Result<String, Infallible> = "str".try_into();
133/// match result {
134///     Ok(string) => println!("{}", string),
135///     Err(inf) => IgnoringErrorHandlerSafe::new().handle_error(inf).await,
136/// }
137///
138/// IgnoringErrorHandlerSafe::new().handle_error(return).await; // return type of `return` is `!` (aka never)
139/// # }
140/// ```
141///
142/// ```compile_fail
143/// use teloxide::dispatching::{ErrorHandler, IgnoringErrorHandlerSafe};
144///
145/// IgnoringErrorHandlerSafe.handle_error(0);
146/// ```
147///
148/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
149/// [`Infallible`]: std::convert::Infallible
150#[derive(Clone, Copy)]
151pub struct IgnoringErrorHandlerSafe;
152
153impl IgnoringErrorHandlerSafe {
154    #[must_use]
155    pub fn new() -> Arc<Self> {
156        Arc::new(Self)
157    }
158}
159
160#[allow(unreachable_code)]
161impl ErrorHandler<Infallible> for IgnoringErrorHandlerSafe {
162    fn handle_error(self: Arc<Self>, _: Infallible) -> BoxFuture<'static, ()> {
163        Box::pin(async {})
164    }
165}
166
167/// A handler that log all errors passed into it.
168///
169/// ## Example
170/// ```
171/// # #[tokio::main]
172/// # async fn main_() {
173/// use teloxide::error_handlers::{ErrorHandler, LoggingErrorHandler};
174///
175/// LoggingErrorHandler::new().handle_error(()).await;
176/// LoggingErrorHandler::with_custom_text("Omg1").handle_error(404).await;
177/// LoggingErrorHandler::with_custom_text("Omg2").handle_error("Invalid data type!").await;
178/// # }
179/// ```
180pub struct LoggingErrorHandler {
181    text: String,
182}
183
184impl LoggingErrorHandler {
185    /// Creates `LoggingErrorHandler` with a meta text before a log.
186    ///
187    /// The logs will be printed in this format: `{text}: {:?}`.
188    #[must_use]
189    pub fn with_custom_text<T>(text: T) -> Arc<Self>
190    where
191        T: Into<String>,
192    {
193        Arc::new(Self { text: text.into() })
194    }
195
196    /// A shortcut for
197    /// `LoggingErrorHandler::with_custom_text("Error".to_owned())`.
198    #[must_use]
199    pub fn new() -> Arc<Self> {
200        Self::with_custom_text("Error".to_owned())
201    }
202}
203
204impl<E> ErrorHandler<E> for LoggingErrorHandler
205where
206    E: Debug,
207{
208    fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()> {
209        log::error!("{text}: {:?}", error, text = self.text);
210        Box::pin(async {})
211    }
212}