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}