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}