Skip to main content

fraiseql_error/
lib.rs

1//! Unified error types for FraiseQL runtime crates.
2//!
3//! All runtime crates depend on this crate for error handling.
4
5// Error variants and fields are self-documenting via their #[error(...)] messages
6#![allow(missing_docs)]
7
8mod auth;
9mod config;
10mod file;
11mod http;
12mod integration;
13mod notification;
14mod observer;
15mod webhook;
16
17pub use auth::AuthError;
18pub use config::ConfigError;
19pub use file::FileError;
20// Re-export for convenience
21pub use http::{ErrorResponse, IntoHttpResponse};
22pub use integration::IntegrationError;
23pub use notification::NotificationError;
24pub use observer::ObserverError;
25pub use webhook::WebhookError;
26
27/// Unified error type wrapping all domain errors
28#[derive(Debug, thiserror::Error)]
29pub enum RuntimeError {
30    #[error(transparent)]
31    Config(#[from] ConfigError),
32
33    #[error(transparent)]
34    Auth(#[from] AuthError),
35
36    #[error(transparent)]
37    Webhook(#[from] WebhookError),
38
39    #[error(transparent)]
40    File(#[from] FileError),
41
42    #[error(transparent)]
43    Notification(#[from] NotificationError),
44
45    #[error(transparent)]
46    Observer(#[from] ObserverError),
47
48    #[error(transparent)]
49    Integration(#[from] IntegrationError),
50
51    #[error("Database error: {0}")]
52    Database(#[from] sqlx::Error),
53
54    #[error("Rate limit exceeded")]
55    RateLimited { retry_after: Option<u64> },
56
57    #[error("Service unavailable: {reason}")]
58    ServiceUnavailable {
59        reason:      String,
60        retry_after: Option<u64>,
61    },
62
63    #[error("Resource not found: {resource}")]
64    NotFound { resource: String },
65
66    #[error("Internal error: {message}")]
67    Internal {
68        message: String,
69        #[source]
70        source:  Option<Box<dyn std::error::Error + Send + Sync>>,
71    },
72}
73
74impl RuntimeError {
75    /// Get the error code for this error
76    pub const fn error_code(&self) -> &'static str {
77        match self {
78            Self::Config(e) => e.error_code(),
79            Self::Auth(e) => e.error_code(),
80            Self::Webhook(e) => e.error_code(),
81            Self::File(e) => e.error_code(),
82            Self::Notification(e) => e.error_code(),
83            Self::Observer(e) => e.error_code(),
84            Self::Integration(e) => e.error_code(),
85            Self::Database(_) => "database_error",
86            Self::RateLimited { .. } => "rate_limited",
87            Self::ServiceUnavailable { .. } => "service_unavailable",
88            Self::NotFound { .. } => "not_found",
89            Self::Internal { .. } => "internal_error",
90        }
91    }
92
93    /// Get documentation URL for this error
94    pub fn docs_url(&self) -> String {
95        format!("https://docs.fraiseql.dev/errors#{}", self.error_code())
96    }
97}