openrouter_rs/error.rs
1//! # Error Handling
2//!
3//! This module provides comprehensive error types for the OpenRouter SDK.
4//! All errors implement the standard library's `Error` trait and can be
5//! used with any error handling framework.
6//!
7//! ## 🎯 Error Categories
8//!
9//! ### HTTP Request Errors
10//! - **`HttpRequest`**: Network-level failures (connectivity, timeouts)
11//! - **`ApiError`**: HTTP status code errors from the API
12//!
13//! ### OpenRouter API Errors
14//! - **`ModerationError`**: Content moderation violations
15//! - **`ProviderError`**: Issues with specific model providers
16//! - **`ApiErrorWithMetadata`**: API errors with additional context
17//!
18//! ### Configuration Errors
19//! - **`ConfigError`**: Configuration parsing or validation issues
20//! - **`ConfigNotFound`**: Missing configuration files
21//! - **`KeyNotConfigured`**: Missing or invalid API keys
22//!
23//! ### Data Processing Errors
24//! - **`UninitializedFieldError`**: Builder pattern validation failures
25//! - **`Serialization`**: JSON serialization/deserialization errors
26//!
27//! ### System Errors
28//! - **`Io`**: File system and I/O operations
29//! - **`Unknown`**: Unexpected errors
30//!
31//! ## 🚀 Usage Examples
32//!
33//! ### Basic Error Handling
34//!
35//! ```rust
36//! use openrouter_rs::{OpenRouterClient, error::OpenRouterError};
37//!
38//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
39//! let client = OpenRouterClient::builder()
40//! .api_key("invalid_key")
41//! .build()?;
42//!
43//! match client.list_models().await {
44//! Ok(models) => println!("Found {} models", models.len()),
45//! Err(OpenRouterError::ApiError { code, message }) => {
46//! eprintln!("API error {}: {}", code, message);
47//! }
48//! Err(OpenRouterError::HttpRequest(e)) => {
49//! eprintln!("Network error: {}", e);
50//! }
51//! Err(e) => eprintln!("Other error: {}", e),
52//! }
53//! # Ok(())
54//! # }
55//! ```
56//!
57//! ### Rate Limiting Handling
58//!
59//! ```rust
60//! use openrouter_rs::error::OpenRouterError;
61//! use surf::StatusCode;
62//!
63//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
64//! match client.send_chat_completion(&request).await {
65//! Err(OpenRouterError::ApiError { code: StatusCode::TooManyRequests, .. }) => {
66//! println!("Rate limited, retrying after delay...");
67//! tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
68//! // Retry logic here
69//! }
70//! Ok(response) => println!("Success!"),
71//! Err(e) => return Err(e.into()),
72//! }
73//! # Ok(())
74//! # }
75//! ```
76//!
77//! ### Configuration Error Handling
78//!
79//! ```rust
80//! use openrouter_rs::{config::load_config, error::OpenRouterError};
81//!
82//! match load_config("./config.toml") {
83//! Ok(config) => println!("Config loaded successfully"),
84//! Err(OpenRouterError::ConfigNotFound(path)) => {
85//! println!("Config file not found at: {}", path.display());
86//! // Use default configuration
87//! }
88//! Err(OpenRouterError::ConfigError(msg)) => {
89//! eprintln!("Invalid configuration: {}", msg);
90//! }
91//! Err(e) => eprintln!("Unexpected error: {}", e),
92//! }
93//! ```
94//!
95//! ## 🔄 Error Conversion
96//!
97//! The SDK automatically converts common error types:
98//!
99//! - `surf::Error` → `OpenRouterError::HttpRequest`
100//! - `serde_json::Error` → `OpenRouterError::Serialization`
101//! - `std::io::Error` → `OpenRouterError::Io`
102//! - `derive_builder::UninitializedFieldError` → `OpenRouterError::UninitializedFieldError`
103
104use std::path::PathBuf;
105
106use serde_json::Value;
107use surf::StatusCode;
108use thiserror::Error;
109
110/// Comprehensive error type for OpenRouter SDK operations
111///
112/// This enum covers all possible error conditions that can occur when
113/// using the OpenRouter SDK, from network issues to API-specific errors.
114/// All variants implement `std::error::Error` and provide detailed
115/// context information.
116///
117/// # Examples
118///
119/// ```rust
120/// use openrouter_rs::error::OpenRouterError;
121/// use surf::StatusCode;
122///
123/// // Pattern matching on specific error types
124/// fn handle_error(error: OpenRouterError) {
125/// match error {
126/// OpenRouterError::ApiError { code: StatusCode::Unauthorized, .. } => {
127/// println!("Check your API key");
128/// }
129/// OpenRouterError::ModerationError { reasons, .. } => {
130/// println!("Content flagged for: {:?}", reasons);
131/// }
132/// OpenRouterError::ConfigError(msg) => {
133/// println!("Configuration issue: {}", msg);
134/// }
135/// _ => println!("Other error: {}", error),
136/// }
137/// }
138/// ```
139#[derive(Error, Debug)]
140pub enum OpenRouterError {
141 // HTTP request errors
142 #[error("HTTP request failed: {0}")]
143 HttpRequest(surf::Error),
144
145 // API response errors
146 #[error("API error: {code}, message: {message}")]
147 ApiError { code: StatusCode, message: String },
148
149 #[error(
150 "Moderation error: {message}, reasons: {reasons:?}, flagged input: {flagged_input}, provider: {provider_name}, model: {model_slug}"
151 )]
152 ModerationError {
153 code: StatusCode,
154 message: String,
155 reasons: Vec<String>,
156 flagged_input: String,
157 provider_name: String,
158 model_slug: String,
159 },
160
161 #[error("Provider error: {message}, provider: {provider_name}, raw: {raw:?}")]
162 ProviderError {
163 code: StatusCode,
164 message: String,
165 provider_name: String,
166 raw: Value,
167 },
168
169 #[error("API error with metadata: {message}, metadata: {metadata:?}")]
170 ApiErrorWithMetadata {
171 code: StatusCode,
172 message: String,
173 metadata: Value,
174 },
175
176 // Configuration errors
177 #[error("Config error: {0}")]
178 ConfigError(String),
179
180 #[error("Config file not found: {0}")]
181 ConfigNotFound(PathBuf),
182
183 #[error("API key not configured")]
184 KeyNotConfigured,
185
186 // Data processing errors
187 #[error("Uninitialized field error: {0}")]
188 UninitializedFieldError(#[from] derive_builder::UninitializedFieldError),
189
190 #[error("Serialization error: {0}")]
191 Serialization(#[from] serde_json::Error),
192
193 // System IO errors
194 #[error("IO error: {0}")]
195 Io(#[from] std::io::Error),
196
197 // Uncategorized errors
198 #[error("Unknown error: {0}")]
199 Unknown(String),
200}
201
202impl From<surf::Error> for OpenRouterError {
203 fn from(err: surf::Error) -> Self {
204 OpenRouterError::HttpRequest(err)
205 }
206}