Skip to main content

fraiseql_error/
notification.rs

1use std::time::Duration;
2
3/// Errors that occur during notification delivery (email, SMS, push, etc.).
4#[derive(Debug, thiserror::Error)]
5#[non_exhaustive]
6pub enum NotificationError {
7    /// The notification provider is misconfigured (e.g. missing API key or
8    /// invalid sender address).
9    #[error("Configuration error: {message}")]
10    Configuration {
11        /// Description of the configuration problem.
12        message: String,
13    },
14
15    /// The notification provider returned an unexpected error response.
16    #[error("Provider error: {provider} - {message}")]
17    Provider {
18        /// Name of the notification provider (e.g. `"sendgrid"`, `"twilio"`).
19        provider: String,
20        /// Error message from the provider (kept server-side; not forwarded to clients).
21        message:  String,
22    },
23
24    /// The notification provider is temporarily unreachable or returning
25    /// 5xx responses.
26    #[error("Provider unavailable: {provider}")]
27    ProviderUnavailable {
28        /// Name of the provider that is unavailable.
29        provider:    String,
30        /// How long to wait before retrying, if the provider indicated a backoff.
31        retry_after: Option<Duration>,
32    },
33
34    /// The notification request contained invalid data (e.g. a malformed
35    /// recipient address or an empty message body).
36    #[error("Invalid input: {message}")]
37    InvalidInput {
38        /// Description of what was invalid.
39        message: String,
40    },
41
42    /// An error occurred while rendering the notification template.
43    #[error("Template error: {message}")]
44    Template {
45        /// Description of the template rendering failure.
46        message: String,
47    },
48
49    /// The notification provider has rate-limited the sending account.
50    #[error("Rate limited by provider: retry after {seconds} seconds")]
51    ProviderRateLimited {
52        /// Name of the provider that applied the rate limit.
53        provider: String,
54        /// Number of seconds to wait before retrying.
55        seconds:  u64,
56    },
57
58    /// The circuit breaker for this provider is open because too many recent
59    /// requests have failed.
60    ///
61    /// Requests will not be forwarded to the provider until `retry_after` has
62    /// elapsed, giving the provider time to recover.
63    #[error("Circuit breaker open for provider: {provider}")]
64    CircuitOpen {
65        /// Name of the provider whose circuit is open.
66        provider:    String,
67        /// How long to wait before the circuit transitions to half-open.
68        retry_after: Duration,
69    },
70
71    /// The notification delivery attempt did not complete within the allowed
72    /// time budget.
73    #[error("Timeout sending notification")]
74    Timeout,
75}
76
77impl NotificationError {
78    /// Returns a short, stable error code string suitable for API responses and
79    /// structured logging.
80    pub const fn error_code(&self) -> &'static str {
81        match self {
82            Self::Configuration { .. } => "notification_config_error",
83            Self::Provider { .. } => "notification_provider_error",
84            Self::ProviderUnavailable { .. } => "notification_provider_unavailable",
85            Self::InvalidInput { .. } => "notification_invalid_input",
86            Self::Template { .. } => "notification_template_error",
87            Self::ProviderRateLimited { .. } => "notification_rate_limited",
88            Self::CircuitOpen { .. } => "notification_circuit_open",
89            Self::Timeout => "notification_timeout",
90        }
91    }
92}