Skip to main content

rustauth_core/options/
api_error.rs

1//! API error handling options (parity with Better Auth `onAPIError`).
2
3use std::fmt;
4use std::sync::Arc;
5
6use crate::api::{ApiRequest, ApiResponse};
7use crate::error::RustAuthError;
8
9/// Configuration for unhandled API errors.
10#[derive(Clone, Default)]
11pub struct OnApiErrorOptions {
12    /// When true, internal errors propagate instead of returning JSON/redirect responses.
13    pub throw: bool,
14    /// Default redirect target for OAuth-style error flows.
15    pub error_url: Option<String>,
16    pub default_error_page: DefaultErrorPage,
17    pub on_error: Option<Arc<dyn OnApiErrorHandler>>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct DefaultErrorPage {
22    pub title: String,
23    pub heading: String,
24    pub message: String,
25}
26
27impl Default for DefaultErrorPage {
28    fn default() -> Self {
29        Self {
30            title: "Error".to_owned(),
31            heading: "Something went wrong".to_owned(),
32            message: "We encountered an unexpected error.".to_owned(),
33        }
34    }
35}
36
37impl DefaultErrorPage {
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    #[must_use]
43    pub fn title(mut self, title: impl Into<String>) -> Self {
44        self.title = title.into();
45        self
46    }
47
48    #[must_use]
49    pub fn heading(mut self, heading: impl Into<String>) -> Self {
50        self.heading = heading.into();
51        self
52    }
53
54    #[must_use]
55    pub fn message(mut self, message: impl Into<String>) -> Self {
56        self.message = message.into();
57        self
58    }
59}
60
61impl fmt::Debug for OnApiErrorOptions {
62    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
63        formatter
64            .debug_struct("OnApiErrorOptions")
65            .field("throw", &self.throw)
66            .field("error_url", &self.error_url)
67            .field("default_error_page", &self.default_error_page)
68            .field(
69                "on_error",
70                &self.on_error.as_ref().map(|_| "<on-api-error>"),
71            )
72            .finish()
73    }
74}
75
76impl OnApiErrorOptions {
77    pub fn new() -> Self {
78        Self::default()
79    }
80
81    #[must_use]
82    pub fn throw(mut self, throw: bool) -> Self {
83        self.throw = throw;
84        self
85    }
86
87    #[must_use]
88    pub fn error_url(mut self, url: impl Into<String>) -> Self {
89        self.error_url = Some(url.into());
90        self
91    }
92
93    #[must_use]
94    pub fn default_error_page(mut self, page: DefaultErrorPage) -> Self {
95        self.default_error_page = page;
96        self
97    }
98
99    #[must_use]
100    pub fn on_error<H>(mut self, handler: H) -> Self
101    where
102        H: OnApiErrorHandler,
103    {
104        self.on_error = Some(Arc::new(handler));
105        self
106    }
107}
108
109/// Hook invoked when the router surfaces an unhandled error.
110pub trait OnApiErrorHandler: Send + Sync + 'static {
111    fn on_error(
112        &self,
113        error: &RustAuthError,
114        request: &ApiRequest,
115    ) -> Result<Option<ApiResponse>, RustAuthError>;
116}
117
118impl<F> OnApiErrorHandler for F
119where
120    F: Fn(&RustAuthError, &ApiRequest) -> Result<Option<ApiResponse>, RustAuthError>
121        + Send
122        + Sync
123        + 'static,
124{
125    fn on_error(
126        &self,
127        error: &RustAuthError,
128        request: &ApiRequest,
129    ) -> Result<Option<ApiResponse>, RustAuthError> {
130        self(error, request)
131    }
132}