klauthed_data/error/
data_error.rs1use klauthed_core::config::{CacheBackend, DbSystem, MessagingBackend};
2use klauthed_core::error::ConfigError;
3use klauthed_error::{DomainError, ErrorCategory, ErrorCode};
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8#[non_exhaustive]
9pub enum DataError {
10 #[error("configuration error: {0}")]
13 Config(#[from] ConfigError),
14
15 #[error("database system '{0:?}' is not supported by this connector")]
17 UnsupportedSystem(DbSystem),
18
19 #[error("cache backend '{0:?}' is not supported by this connector")]
21 UnsupportedCacheBackend(CacheBackend),
22
23 #[error("messaging backend '{0:?}' is not supported by this connector")]
25 UnsupportedMessagingBackend(MessagingBackend),
26
27 #[error("no connection URL could be derived for {0}")]
29 MissingUrl(&'static str),
30
31 #[error("messaging setup error: {0}")]
33 Messaging(String),
34
35 #[error("transactional outbox error: {0}")]
37 Outbox(String),
38
39 #[error("idempotency store error: {0}")]
41 Idempotency(String),
42
43 #[error("lock '{0}' is already held")]
45 LockHeld(String),
46
47 #[error("this backend requires the '{0}' cargo feature to be enabled")]
49 FeatureDisabled(&'static str),
50
51 #[error("I/O error: {0}")]
53 Io(#[from] std::io::Error),
54
55 #[cfg(feature = "sql")]
57 #[error("database error: {0}")]
58 Sqlx(#[from] sqlx::Error),
59
60 #[cfg(feature = "redis")]
62 #[error("redis error: {0}")]
63 Redis(#[from] ::redis::RedisError),
64
65 #[cfg(feature = "nats")]
67 #[error("nats connection error: {0}")]
68 Nats(#[from] async_nats::ConnectError),
69
70 #[cfg(feature = "rabbitmq")]
72 #[error("rabbitmq connection error: {0}")]
73 RabbitMq(#[from] lapin::Error),
74
75 #[cfg(feature = "kafka")]
77 #[error("kafka connection error: {0}")]
78 Kafka(#[from] rskafka::client::error::Error),
79
80 #[cfg(feature = "storage")]
82 #[error("storage error: {0}")]
83 Storage(#[from] object_store::Error),
84
85 #[error("invalid pagination request: {0}")]
87 InvalidPage(String),
88
89 #[error("invalid or corrupted cursor: {0}")]
91 InvalidCursor(String),
92}
93
94impl DataError {
95 fn classify(&self) -> (ErrorCategory, ErrorCode) {
99 use ErrorCategory::Internal;
100 match self {
101 DataError::Config(e) => (e.category(), e.code()),
102 DataError::UnsupportedSystem(_) => {
103 (Internal, ErrorCode::new("data.unsupported_system"))
104 }
105 DataError::UnsupportedCacheBackend(_) => {
106 (Internal, ErrorCode::new("data.unsupported_cache_backend"))
107 }
108 DataError::UnsupportedMessagingBackend(_) => {
109 (Internal, ErrorCode::new("data.unsupported_messaging_backend"))
110 }
111 DataError::MissingUrl(_) => (Internal, ErrorCode::new("data.missing_url")),
112 DataError::Messaging(_) => (Internal, ErrorCode::new("data.messaging")),
113 DataError::Outbox(_) => (Internal, ErrorCode::new("data.outbox")),
114 DataError::Idempotency(_) => (Internal, ErrorCode::new("data.idempotency")),
115 DataError::LockHeld(_) => (ErrorCategory::Conflict, ErrorCode::new("data.lock_held")),
117 DataError::FeatureDisabled(_) => (Internal, ErrorCode::new("data.feature_disabled")),
118 DataError::Io(_) => (Internal, ErrorCode::new("data.io")),
119 #[cfg(feature = "sql")]
121 DataError::Sqlx(_) => (ErrorCategory::Unavailable, ErrorCode::new("data.database")),
122 #[cfg(feature = "redis")]
123 DataError::Redis(_) => (ErrorCategory::Unavailable, ErrorCode::new("data.redis")),
124 #[cfg(feature = "nats")]
125 DataError::Nats(_) => (ErrorCategory::Unavailable, ErrorCode::new("data.nats")),
126 #[cfg(feature = "rabbitmq")]
127 DataError::RabbitMq(_) => (ErrorCategory::Unavailable, ErrorCode::new("data.rabbitmq")),
128 #[cfg(feature = "kafka")]
129 DataError::Kafka(_) => (ErrorCategory::Unavailable, ErrorCode::new("data.kafka")),
130 #[cfg(feature = "storage")]
131 DataError::Storage(_) => (Internal, ErrorCode::new("data.storage")),
132 DataError::InvalidPage(_) => {
133 (ErrorCategory::BadRequest, ErrorCode::new("data.invalid_page"))
134 }
135 DataError::InvalidCursor(_) => {
136 (ErrorCategory::BadRequest, ErrorCode::new("data.invalid_cursor"))
137 }
138 }
139 }
140}
141
142impl DomainError for DataError {
143 fn category(&self) -> ErrorCategory {
144 self.classify().0
145 }
146
147 fn code(&self) -> ErrorCode {
148 self.classify().1
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn unsupported_system_is_internal() {
158 let err = DataError::UnsupportedSystem(DbSystem::MongoDb);
159 assert_eq!(err.category(), ErrorCategory::Internal);
160 assert_eq!(err.code().as_str(), "data.unsupported_system");
161 }
162
163 #[test]
164 fn wrapped_config_error_delegates_classification() {
165 let err: DataError = ConfigError::MissingRequired("database".into()).into();
166 assert_eq!(err.category(), ErrorCategory::Internal);
168 assert_eq!(err.code().as_str(), "config.missing_required");
169 }
170}