openai_ergonomic/
errors.rs

1//! Error types for the `OpenAI` ergonomic wrapper.
2//!
3//! This module provides comprehensive error handling with detailed error
4//! information and proper error chaining.
5
6use thiserror::Error;
7
8/// Result type used throughout the crate.
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// Main error type for the `OpenAI` ergonomic wrapper.
12#[derive(Error, Debug)]
13pub enum Error {
14    /// Invalid request parameters or configuration.
15    #[error("Invalid request: {0}")]
16    InvalidRequest(String),
17
18    /// Authentication errors.
19    #[error("Authentication failed: {0}")]
20    Authentication(String),
21
22    /// Rate limiting errors.
23    #[error("Rate limit exceeded: {0}")]
24    RateLimit(String),
25
26    /// HTTP client errors.
27    #[error("HTTP error: {0}")]
28    Http(#[from] reqwest::Error),
29
30    /// JSON serialization/deserialization errors.
31    #[error("JSON error: {0}")]
32    Json(#[from] serde_json::Error),
33
34    /// `OpenAI` API errors with status code and message.
35    #[error("`OpenAI` API error (status {status}): {message}")]
36    Api {
37        /// HTTP status code returned by the API
38        status: u16,
39        /// Error message from the API
40        message: String,
41        /// Type of error (if provided by API)
42        error_type: Option<String>,
43        /// Error code (if provided by API)
44        error_code: Option<String>,
45    },
46
47    /// Streaming errors.
48    #[error("Stream error: {0}")]
49    Stream(String),
50
51    /// File operation errors.
52    #[error("File error: {0}")]
53    File(#[from] std::io::Error),
54
55    /// Configuration errors.
56    #[error("Configuration error: {0}")]
57    Config(String),
58
59    /// Builder validation errors.
60    #[error("Builder validation error: {0}")]
61    Builder(String),
62
63    /// Generic internal errors.
64    #[error("Internal error: {0}")]
65    Internal(String),
66}
67
68impl Error {
69    /// Create a new API error with status and message.
70    pub fn api(status: u16, message: impl Into<String>) -> Self {
71        Self::Api {
72            status,
73            message: message.into(),
74            error_type: None,
75            error_code: None,
76        }
77    }
78
79    /// Create a new API error with full details.
80    pub fn api_detailed(
81        status: u16,
82        message: impl Into<String>,
83        error_type: Option<String>,
84        error_code: Option<String>,
85    ) -> Self {
86        Self::Api {
87            status,
88            message: message.into(),
89            error_type,
90            error_code,
91        }
92    }
93
94    /// Check if this is a rate limit error.
95    pub fn is_rate_limit(&self) -> bool {
96        matches!(self, Error::RateLimit(_)) || matches!(self, Error::Api { status: 429, .. })
97    }
98
99    /// Check if this is an authentication error.
100    pub fn is_auth_error(&self) -> bool {
101        matches!(self, Error::Authentication(_)) || matches!(self, Error::Api { status: 401, .. })
102    }
103
104    /// Check if this is a client error (4xx status codes).
105    pub fn is_client_error(&self) -> bool {
106        match self {
107            Error::Api { status, .. } => (400..500).contains(status),
108            Error::Authentication(_) | Error::RateLimit(_) | Error::InvalidRequest(_) => true,
109            _ => false,
110        }
111    }
112
113    /// Check if this is a server error (5xx status codes).
114    pub fn is_server_error(&self) -> bool {
115        match self {
116            Error::Api { status, .. } => (500..600).contains(status),
117            _ => false,
118        }
119    }
120
121    /// Check if this error might be retryable.
122    pub fn is_retryable(&self) -> bool {
123        self.is_rate_limit() || self.is_server_error()
124    }
125}
126
127/// Specialized error types for different API endpoints.
128pub mod chat {
129    use super::Error;
130
131    /// Create an error for invalid chat messages.
132    pub fn invalid_messages(msg: impl Into<String>) -> Error {
133        Error::InvalidRequest(format!("Invalid chat messages: {}", msg.into()))
134    }
135
136    /// Create an error for unsupported model.
137    pub fn unsupported_model(model: impl Into<String>) -> Error {
138        Error::InvalidRequest(format!("Unsupported model: {}", model.into()))
139    }
140}
141
142/// Specialized error types for responses API.
143pub mod responses {
144    use super::Error;
145
146    /// Create an error for invalid tool definition.
147    pub fn invalid_tool(msg: impl Into<String>) -> Error {
148        Error::InvalidRequest(format!("Invalid tool definition: {}", msg.into()))
149    }
150
151    /// Create an error for missing required response format.
152    pub fn missing_response_format() -> Error {
153        Error::InvalidRequest("Response format is required for structured outputs".to_string())
154    }
155}
156
157/// Specialized error types for file operations.
158pub mod files {
159    use super::Error;
160
161    /// Create an error for file upload failures.
162    pub fn upload_failed(msg: impl Into<String>) -> Error {
163        Error::File(std::io::Error::new(
164            std::io::ErrorKind::Other,
165            format!("File upload failed: {}", msg.into()),
166        ))
167    }
168
169    /// Create an error for unsupported file type.
170    pub fn unsupported_type(file_type: impl Into<String>) -> Error {
171        Error::InvalidRequest(format!("Unsupported file type: {}", file_type.into()))
172    }
173}
174
175/// Specialized error types for streaming operations.
176pub mod streaming {
177    use super::Error;
178
179    /// Create an error for stream connection failures.
180    pub fn connection_failed(msg: impl Into<String>) -> Error {
181        Error::Stream(format!("Stream connection failed: {}", msg.into()))
182    }
183
184    /// Create an error for stream parsing failures.
185    pub fn parse_failed(msg: impl Into<String>) -> Error {
186        Error::Stream(format!("Stream parsing failed: {}", msg.into()))
187    }
188}