atproto_client/errors.rs
1//! # Structured Error Types
2//!
3//! Comprehensive error handling for AT Protocol client operations using structured error types
4//! with the `thiserror` library. All errors follow the project convention of prefixed error codes
5//! with descriptive messages.
6//!
7//! ## Error Categories
8//!
9//! - **`ClientError`** (http-1 to http-2): HTTP client operation errors including request failures and parsing errors
10//! - **`DPoPError`** (auth-1 to auth-3): DPoP authentication related errors
11//! - **`CliError`** (cli-1 to cli-4): Command-line interface specific errors including file I/O and resolution failures
12//!
13//! ## Error Format
14//!
15//! All errors use the standardized format: `error-atproto-client-{domain}-{number} {message}: {details}`
16
17use std::collections::HashMap;
18
19use serde::{Deserialize, Serialize};
20use thiserror::Error;
21
22/// Simple error response structure for AT Protocol APIs.
23///
24/// This structure represents the standard error response format used by AT Protocol
25/// services, allowing for flexible error reporting with optional fields and
26/// extension points for additional error context.
27#[cfg_attr(debug_assertions, derive(Debug))]
28#[derive(Clone, Serialize, Deserialize)]
29pub struct SimpleError {
30 /// The error code identifier
31 pub error: Option<String>,
32 /// Human-readable description of the error
33 pub error_description: Option<String>,
34 /// Additional error message details
35 pub message: Option<String>,
36
37 /// Additional error fields that don't fit standard structure
38 #[serde(flatten)]
39 pub extra: HashMap<String, serde_json::Value>,
40}
41
42impl SimpleError {
43 /// Combines all available error information into a single message.
44 ///
45 /// Concatenates the error code, description, and message fields with
46 /// colons to provide a comprehensive error description.
47 pub fn error_message(&self) -> String {
48 [&self.error, &self.error_description, &self.message]
49 .iter()
50 .filter_map(|v| (*v).clone())
51 .collect::<Vec<String>>()
52 .join(": ")
53 }
54}
55
56/// Error types that can occur during HTTP client operations.
57///
58/// These errors represent failures in basic HTTP operations such as
59/// making requests and parsing responses for unauthenticated operations.
60#[derive(Debug, Error)]
61pub enum ClientError {
62 /// Occurs when an HTTP request fails
63 #[error("error-atproto-client-http-1 HTTP request failed: {url} {error}")]
64 HttpRequestFailed {
65 /// The URL that was requested
66 url: String,
67 /// The underlying HTTP error
68 error: reqwest::Error,
69 },
70
71 /// Occurs when JSON parsing from HTTP response fails
72 #[error("error-atproto-client-http-2 JSON parsing failed: {url} {error}")]
73 JsonParseFailed {
74 /// The URL that was requested
75 url: String,
76 /// The underlying parse error
77 error: reqwest::Error,
78 },
79
80 /// Occurs when streaming response bytes fails
81 #[error("error-atproto-client-http-3 Failed to stream response bytes: {url} {error}")]
82 ByteStreamFailed {
83 /// The URL that was requested
84 url: String,
85 /// The underlying streaming error
86 error: reqwest::Error,
87 },
88
89 /// Occurs when an invalid authentication method is used for an operation
90 #[error("error-atproto-client-http-4 Invalid authentication method: {method}")]
91 InvalidAuthMethod {
92 /// Description of the authentication requirement
93 method: String,
94 },
95}
96
97/// Error types that can occur during DPoP authentication operations.
98///
99/// These errors represent failures in authenticated HTTP operations using
100/// DPoP (Demonstration of Proof-of-Possession) for client authentication.
101#[derive(Debug, Error)]
102pub enum DPoPError {
103 /// Occurs when DPoP proof generation fails
104 #[error("error-atproto-client-auth-1 DPoP proof generation failed: {error}")]
105 ProofGenerationFailed {
106 /// The underlying error from DPoP operations
107 error: anyhow::Error,
108 },
109
110 /// Occurs when DPoP authenticated HTTP request fails
111 #[error("error-atproto-client-auth-2 DPoP HTTP request failed: {url} {error}")]
112 HttpRequestFailed {
113 /// The URL that was requested
114 url: String,
115 /// The underlying HTTP error from middleware
116 error: reqwest_middleware::Error,
117 },
118
119 /// Occurs when JSON parsing from DPoP authenticated response fails
120 #[error("error-atproto-client-auth-3 DPoP JSON parsing failed: {url} {error}")]
121 JsonParseFailed {
122 /// The URL that was requested
123 url: String,
124 /// The underlying parse error
125 error: reqwest::Error,
126 },
127}
128
129/// Error types that can occur in CLI tools.
130///
131/// These errors represent failures specific to command-line interface operations
132/// such as file I/O, JSON parsing from files, and DID document resolution.
133#[derive(Debug, Error)]
134pub enum CliError {
135 /// Occurs when reading a file fails
136 #[error("error-atproto-client-cli-1 Failed to read file: {path}")]
137 FileReadFailed {
138 /// The file path that failed to read
139 path: String,
140 },
141
142 /// Occurs when parsing JSON from a file fails
143 #[error("error-atproto-client-cli-2 Failed to parse JSON from file: {path}")]
144 JsonParseFromFileFailed {
145 /// The file path containing invalid JSON
146 path: String,
147 },
148
149 /// Occurs when no PDS endpoint is found in DID document
150 #[error("error-atproto-client-cli-3 No PDS endpoint found in DID document for: {did}")]
151 NoPdsEndpointFound {
152 /// The DID that was resolved
153 did: String,
154 },
155
156 /// Occurs when no JSON data is provided for a procedure call
157 #[error("error-atproto-client-cli-4 No JSON data provided for procedure call")]
158 NoJsonDataProvided,
159}