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}