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