Skip to main content

rotastellar/
error.rs

1//! RotaStellar SDK - Custom Errors
2//!
3//! All custom errors raised by the RotaStellar SDK.
4
5use std::fmt;
6use thiserror::Error;
7
8/// Base error for all RotaStellar SDK errors.
9#[derive(Error, Debug)]
10pub enum RotaStellarError {
11    /// Authentication error
12    #[error("Authentication error: {0}")]
13    Authentication(#[from] AuthenticationError),
14
15    /// API error
16    #[error("API error: {0}")]
17    Api(#[from] ApiError),
18
19    /// Validation error
20    #[error("Validation error: {0}")]
21    Validation(#[from] ValidationError),
22
23    /// Network error
24    #[error("Network error: {0}")]
25    Network(#[from] NetworkError),
26}
27
28/// Authentication errors.
29#[derive(Error, Debug)]
30pub enum AuthenticationError {
31    /// API key is missing or empty.
32    #[error("API key is required. Get your key at https://rotastellar.com/dashboard")]
33    MissingApiKey,
34
35    /// API key format is invalid.
36    #[error("Invalid API key format: {masked_key}. Keys should start with 'rs_live_' or 'rs_test_'")]
37    InvalidApiKey { masked_key: String },
38}
39
40impl AuthenticationError {
41    /// Create a new InvalidApiKey error with a masked key.
42    pub fn invalid_api_key(api_key: &str) -> Self {
43        let masked = if api_key.len() > 10 {
44            format!("{}...", &api_key[..10])
45        } else {
46            api_key.to_string()
47        };
48        Self::InvalidApiKey { masked_key: masked }
49    }
50}
51
52/// API errors returned by the RotaStellar API.
53#[derive(Error, Debug)]
54pub struct ApiError {
55    /// Error message
56    pub message: String,
57    /// HTTP status code
58    pub status_code: u16,
59    /// Request ID for debugging
60    pub request_id: Option<String>,
61    /// Additional details
62    pub details: Option<serde_json::Value>,
63}
64
65impl fmt::Display for ApiError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        write!(f, "[{}] {}", self.status_code, self.message)?;
68        if let Some(ref id) = self.request_id {
69            write!(f, " (request_id: {})", id)?;
70        }
71        Ok(())
72    }
73}
74
75impl ApiError {
76    /// Create a new API error.
77    pub fn new(message: impl Into<String>, status_code: u16) -> Self {
78        Self {
79            message: message.into(),
80            status_code,
81            request_id: None,
82            details: None,
83        }
84    }
85
86    /// Create a rate limit error.
87    pub fn rate_limited(retry_after: Option<u32>) -> Self {
88        Self {
89            message: "Rate limit exceeded".to_string(),
90            status_code: 429,
91            request_id: None,
92            details: retry_after.map(|r| serde_json::json!({ "retry_after": r })),
93        }
94    }
95
96    /// Create a not found error.
97    pub fn not_found(resource_type: &str, resource_id: &str) -> Self {
98        Self {
99            message: format!("{} not found: {}", resource_type, resource_id),
100            status_code: 404,
101            request_id: None,
102            details: Some(serde_json::json!({
103                "resource_type": resource_type,
104                "resource_id": resource_id
105            })),
106        }
107    }
108
109    /// Set the request ID.
110    pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
111        self.request_id = Some(request_id.into());
112        self
113    }
114}
115
116/// Validation errors for input data.
117#[derive(Error, Debug)]
118#[error("Validation error on '{field}': {message}")]
119pub struct ValidationError {
120    /// Field that failed validation
121    pub field: String,
122    /// Error message
123    pub message: String,
124}
125
126impl ValidationError {
127    /// Create a new validation error.
128    pub fn new(field: impl Into<String>, message: impl Into<String>) -> Self {
129        Self {
130            field: field.into(),
131            message: message.into(),
132        }
133    }
134}
135
136/// Network errors.
137#[derive(Error, Debug)]
138pub enum NetworkError {
139    /// Request timed out.
140    #[error("Request timed out after {0} seconds")]
141    Timeout(f64),
142
143    /// Connection failed.
144    #[error("Connection failed: {0}")]
145    Connection(String),
146
147    /// Other network error.
148    #[error("Network error: {0}")]
149    Other(String),
150}
151
152/// Result type alias for RotaStellar operations.
153pub type Result<T> = std::result::Result<T, RotaStellarError>;