A decentralized event management and credentialing system built on atproto.
1//! Core error types for the Acudo application.
2//!
3//! This module provides the main error hierarchy and shared error types used
4//! throughout the application. All error strings follow the standardized format:
5//!
6//! ```text
7//! error-acudo-<domain>-<number> <message>: <details>
8//! ```
9//!
10//! ## Error Domain Allocation
11//!
12//! To ensure globally unique error identifiers, domains are allocated as follows:
13//! - `config`: 1-99 - Configuration and environment variable errors
14//! - `http`: 100-199 - HTTP layer and web server errors
15//! - `storage`: 200-299 - Database and storage errors
16//! - `auth`: 300-399 - Authentication and authorization errors
17//! - `event`: 400-499 - Event management and AT Protocol errors
18//! - `identity`: 500-599 - DID resolution and identity errors
19//! - `oauth`: 600-699 - OAuth flow and session errors
20
21use std::net::AddrParseError;
22use thiserror::Error;
23
24/// Core application errors that can occur across multiple domains.
25///
26/// This enum serves as the main error type for the application, aggregating
27/// all domain-specific errors into a single hierarchy.
28#[derive(Debug, Error)]
29pub enum AcudoError {
30 /// Configuration-related errors (domain: config, range: 1-99)
31 #[error(transparent)]
32 Config(#[from] ConfigError),
33
34 /// HTTP layer errors (domain: http, range: 100-199)
35 #[error(transparent)]
36 Http(#[from] HttpError),
37
38 /// Storage and database errors (domain: storage, range: 200-299)
39 #[error(transparent)]
40 Storage(#[from] StorageError),
41
42 /// Event management errors (domain: event, range: 400-499)
43 #[error(transparent)]
44 Event(#[from] EventError),
45
46 /// Identity resolution errors (domain: identity, range: 500-599)
47 #[error(transparent)]
48 Identity(#[from] IdentityError),
49
50 /// OAuth and authentication errors (domain: oauth, range: 600-699)
51 #[error(transparent)]
52 OAuth(#[from] OAuthError),
53}
54
55/// Configuration validation and environment variable errors.
56///
57/// Error domain: `config` (numbers 1-99)
58#[derive(Error, Debug)]
59pub enum ConfigError {
60 #[error("error-acudo-config-1 Required environment variable not set: {var_name}")]
61 EnvVarRequired { var_name: String },
62
63 #[error("error-acudo-config-2 Failed to parse port: {source}")]
64 PortParsingFailed {
65 #[source]
66 source: std::num::ParseIntError,
67 },
68
69 #[error("error-acudo-config-3 Failed to parse nameserver '{nameserver}': {source}")]
70 NameserverParsingFailed {
71 nameserver: String,
72 #[source]
73 source: AddrParseError,
74 },
75
76 #[error("error-acudo-config-4 No signing keys provided")]
77 EmptySigningKeys,
78
79 #[error("error-acudo-config-5 Missing event AT-URI")]
80 MissingEventAturi,
81
82 #[error("error-acudo-config-6 Invalid event AT-URI format: {aturi}")]
83 InvalidEventAturi { aturi: String },
84
85 #[error("error-acudo-config-7 Missing issuer DID")]
86 MissingIssuerDid,
87
88 #[error("error-acudo-config-8 Invalid issuer DID format: {did}")]
89 InvalidIssuerDid { did: String },
90
91 #[error("error-acudo-config-9 Version not set")]
92 VersionNotSet,
93
94 #[error("error-acudo-config-10 Invalid HTTP configuration: {details}")]
95 InvalidHttpConfig { details: String },
96
97 #[error("error-acudo-config-11 Invalid key format: {details}")]
98 InvalidKeyFormat { details: String },
99
100 #[error("error-acudo-config-12 Version not available")]
101 VersionNotAvailable,
102
103 #[error("error-acudo-config-13 Invalid port number: {port}")]
104 InvalidPortNumber { port: String },
105
106 #[error("error-acudo-config-14 Invalid IP address: {address}")]
107 InvalidIpAddress { address: String },
108
109 #[error("error-acudo-config-15 Invalid timeout value: {value}")]
110 InvalidTimeout { value: String },
111
112 #[error("error-acudo-config-16 Invalid AT URI: {uri}")]
113 InvalidAtUri { uri: String },
114
115 #[error("error-acudo-config-17 Invalid OAuth backend: {backend}")]
116 InvalidOAuthBackend { backend: String },
117
118 #[error("error-acudo-config-18 Invalid DID: {did}")]
119 InvalidDid { did: String },
120}
121
122/// HTTP layer and web server errors.
123///
124/// Error domain: `http` (numbers 100-199)
125#[derive(Debug, Error)]
126pub enum HttpError {
127 #[error("error-acudo-http-100 Unhandled web error: {source}")]
128 Unhandled {
129 #[source]
130 source: anyhow::Error,
131 },
132
133 #[error("error-acudo-http-101 Request validation failed: {details}")]
134 RequestValidation { details: String },
135
136 #[error("error-acudo-http-102 Authentication required")]
137 AuthenticationRequired,
138
139 #[error("error-acudo-http-103 Forbidden access to resource")]
140 Forbidden,
141
142 #[error("error-acudo-http-104 Resource not found")]
143 NotFound,
144}
145
146/// Storage and database operation errors.
147///
148/// Error domain: `storage` (numbers 200-299)
149#[derive(Debug, Error)]
150pub enum StorageError {
151 #[error("error-acudo-storage-200 Database connection failed: {source}")]
152 ConnectionFailed {
153 #[source]
154 source: sqlx::Error,
155 },
156
157 #[error("error-acudo-storage-201 Transaction failed: {source}")]
158 TransactionFailed {
159 #[source]
160 source: sqlx::Error,
161 },
162
163 #[error("error-acudo-storage-202 Query execution failed: {source}")]
164 QueryFailed {
165 #[source]
166 source: sqlx::Error,
167 },
168
169 #[error("error-acudo-storage-203 Record not found: {record_type}")]
170 RecordNotFound { record_type: String },
171
172 #[error("error-acudo-storage-204 Invalid input data: {details}")]
173 InvalidInput { details: String },
174
175 #[error("error-acudo-storage-205 Serialization failed: {source}")]
176 SerializationFailed {
177 #[source]
178 source: anyhow::Error,
179 },
180}
181
182/// Event management and AT Protocol record errors.
183///
184/// Error domain: `event` (numbers 400-499)
185#[derive(Debug, Error)]
186pub enum EventError {
187 #[error("error-acudo-event-400 Invalid AT URI format: {uri}")]
188 InvalidAtUri { uri: String },
189
190 #[error("error-acudo-event-401 AT URI must have 3 parts (repo/collection/rkey): {uri}")]
191 MalformedAtUri { uri: String },
192
193 #[error("error-acudo-event-402 Failed to fetch AT Protocol record: {source}")]
194 RecordFetchFailed {
195 #[source]
196 source: anyhow::Error,
197 },
198
199 #[error("error-acudo-event-403 Failed to parse event record: {source}")]
200 RecordParseFailed {
201 #[source]
202 source: serde_json::Error,
203 },
204
205 #[error("error-acudo-event-404 No PDS service endpoint found in DID document: {did}")]
206 NoPdsEndpoint { did: String },
207
208 #[error("error-acudo-event-405 Identity resolution failed for DID {did}: {source}")]
209 IdentityResolution {
210 did: String,
211 #[source]
212 source: anyhow::Error,
213 },
214
215 #[error("error-acudo-event-406 Failed to fetch record from {uri}: {source}")]
216 RecordFetch {
217 uri: String,
218 #[source]
219 source: anyhow::Error,
220 },
221
222 #[error("error-acudo-event-407 Failed to parse event record from {uri}: {source}")]
223 EventParsing {
224 uri: String,
225 #[source]
226 source: anyhow::Error,
227 },
228
229 #[error("error-acudo-event-408 Record not found at {uri}: {details}")]
230 RecordNotFound { uri: String, details: String },
231}
232
233/// Identity resolution and DID processing errors.
234///
235/// Error domain: `identity` (numbers 500-599)
236#[derive(Debug, Error)]
237pub enum IdentityError {
238 #[error("error-acudo-identity-500 Failed to resolve DID: {did}")]
239 ResolutionFailed {
240 did: String,
241 #[source]
242 source: anyhow::Error,
243 },
244
245 #[error("error-acudo-identity-501 Invalid DID format: {did}")]
246 InvalidDidFormat { did: String },
247
248 #[error("error-acudo-identity-502 DID document missing required service: {service_type}")]
249 MissingService { service_type: String },
250}
251
252/// OAuth flow and session management errors.
253///
254/// Error domain: `oauth` (numbers 600-699)
255#[derive(Debug, Error)]
256pub enum OAuthError {
257 #[error("error-acudo-oauth-600 OAuth state cannot be empty")]
258 EmptyState,
259
260 #[error("error-acudo-oauth-601 Issuer cannot be empty")]
261 EmptyIssuer,
262
263 #[error("error-acudo-oauth-602 DID cannot be empty")]
264 EmptyDid,
265
266 #[error("error-acudo-oauth-603 Session chain cannot be empty")]
267 EmptySessionChain,
268
269 #[error("error-acudo-oauth-604 Access token cannot be empty")]
270 EmptyAccessToken,
271
272 #[error("error-acudo-oauth-605 OAuth request not found: {state}")]
273 RequestNotFound { state: String },
274
275 #[error("error-acudo-oauth-606 OAuth session not found: {session_chain}")]
276 SessionNotFound { session_chain: String },
277}