rain_sdk/error.rs
1//! Error types for the Rain SDK
2//!
3//! This module defines all error types used throughout the Rain SDK, including
4//! HTTP errors, API errors, and validation errors.
5//!
6//! # API Error Codes
7//!
8//! The Rain API uses the following HTTP status codes for errors:
9//!
10//! - `400` - Invalid request (bad request parameters or body)
11//! - `401` - Invalid authorization / Unauthorized (missing or invalid API key)
12//! - `403` - Forbidden (insufficient permissions)
13//! - `404` - Not found (resource not found: User, Card, Company, Transaction, Team, etc.)
14//! - `409` - Conflict (e.g., "Company already has a contract on this chain", "User already has a contract on this chain", "Another active signature already exists")
15//! - `423` - Locked (e.g., "User address is locked")
16//! - `500` - Internal server error
17
18use serde::{Deserialize, Serialize};
19use std::fmt;
20
21/// Main error type for the Rain SDK
22#[derive(Debug, thiserror::Error)]
23pub enum RainError {
24 /// HTTP client errors
25 #[error("HTTP error: {0}")]
26 HttpError(#[from] reqwest::Error),
27
28 /// API error responses from the server
29 ///
30 /// Contains the HTTP status code and error details from the API.
31 /// Common status codes:
32 /// - 400: Invalid request
33 /// - 401: Invalid authorization / Unauthorized
34 /// - 403: Forbidden
35 /// - 404: Not found
36 /// - 409: Conflict
37 /// - 423: Locked
38 /// - 500: Internal server error
39 #[error("API error (status {status}): {response}")]
40 ApiError {
41 /// HTTP status code
42 status: u16,
43 /// Error response details
44 response: Box<ApiErrorResponse>,
45 },
46
47 /// Authentication errors
48 #[error("Authentication error: {0}")]
49 AuthError(String),
50
51 /// Request validation errors
52 #[error("Validation error: {0}")]
53 ValidationError(String),
54
55 /// JSON deserialization errors
56 #[error("Deserialization error: {0}")]
57 DeserializationError(#[from] serde_json::Error),
58
59 /// Other errors
60 #[error("Error: {0}")]
61 Other(#[from] anyhow::Error),
62}
63
64/// API error response structure
65///
66/// This structure represents error responses from the Rain API.
67/// The API may return different error formats, so all fields are optional.
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct ApiErrorResponse {
70 /// Error message describing what went wrong
71 pub message: Option<String>,
72
73 /// Error code (may be a string identifier or numeric code)
74 pub code: Option<String>,
75
76 /// Additional error details (structured data)
77 pub details: Option<serde_json::Value>,
78}
79
80impl ApiErrorResponse {
81 /// Create a new API error response with a message
82 pub fn new(message: String) -> Self {
83 Self {
84 message: Some(message),
85 code: None,
86 details: None,
87 }
88 }
89
90 /// Create a new API error response with a message and code
91 pub fn with_code(message: String, code: String) -> Self {
92 Self {
93 message: Some(message),
94 code: Some(code),
95 details: None,
96 }
97 }
98}
99
100impl fmt::Display for ApiErrorResponse {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 if let Some(ref message) = self.message {
103 write!(f, "{message}")?;
104 } else if let Some(ref code) = self.code {
105 write!(f, "{code}")?;
106 } else {
107 write!(f, "API error")?;
108 }
109 Ok(())
110 }
111}
112
113impl std::error::Error for ApiErrorResponse {}
114
115impl From<ApiErrorResponse> for RainError {
116 fn from(err: ApiErrorResponse) -> Self {
117 // Default to 500 if status is not available
118 RainError::ApiError {
119 status: 500,
120 response: Box::new(err),
121 }
122 }
123}
124
125/// Result type alias for Rain SDK operations
126pub type Result<T> = std::result::Result<T, RainError>;
127
128impl From<serde_urlencoded::ser::Error> for RainError {
129 fn from(err: serde_urlencoded::ser::Error) -> Self {
130 RainError::ValidationError(format!("URL encoding error: {err}"))
131 }
132}