serper_sdk/core/
error.rs

1/// Error handling module for the Serper SDK
2///
3/// This module defines all error types that can occur within the SDK,
4/// providing comprehensive error handling with detailed context.
5use thiserror::Error;
6
7/// Main error type for the Serper SDK
8///
9/// This enum covers all possible error conditions that can occur
10/// when using the SDK, from network issues to API-specific errors.
11#[derive(Error, Debug)]
12pub enum SerperError {
13    /// HTTP request failed
14    ///
15    /// This error wraps underlying HTTP transport errors
16    #[error("HTTP request failed: {0}")]
17    Request(#[from] reqwest::Error),
18
19    /// JSON parsing failed
20    ///
21    /// This error occurs when the API returns invalid JSON
22    /// or when serialization/deserialization fails
23    #[error("JSON parsing failed: {0}")]
24    Json(#[from] serde_json::Error),
25
26    /// API returned an error response
27    ///
28    /// This error represents HTTP error status codes and API-specific errors
29    #[error("API error: {message}")]
30    Api {
31        /// The error message from the API or HTTP status description
32        message: String,
33    },
34
35    /// Invalid API key provided
36    ///
37    /// This error occurs when the API key is empty, malformed, or rejected
38    #[error("Invalid API key")]
39    InvalidApiKey,
40
41    /// Configuration error
42    ///
43    /// This error occurs when SDK configuration is invalid
44    #[error("Configuration error: {message}")]
45    Config {
46        /// Description of the configuration issue
47        message: String,
48    },
49
50    /// Validation error
51    ///
52    /// This error occurs when input parameters are invalid
53    #[error("Validation error: {message}")]
54    Validation {
55        /// Description of the validation issue
56        message: String,
57    },
58}
59
60impl SerperError {
61    /// Creates a new API error with a custom message
62    pub fn api_error(message: impl Into<String>) -> Self {
63        Self::Api {
64            message: message.into(),
65        }
66    }
67
68    /// Creates a new configuration error
69    pub fn config_error(message: impl Into<String>) -> Self {
70        Self::Config {
71            message: message.into(),
72        }
73    }
74
75    /// Creates a new validation error
76    pub fn validation_error(message: impl Into<String>) -> Self {
77        Self::Validation {
78            message: message.into(),
79        }
80    }
81
82    /// Checks if the error is related to authentication
83    pub fn is_auth_error(&self) -> bool {
84        matches!(self, SerperError::InvalidApiKey)
85    }
86
87    /// Checks if the error is related to network/transport
88    pub fn is_network_error(&self) -> bool {
89        matches!(self, SerperError::Request(_))
90    }
91
92    /// Checks if the error is related to data parsing
93    pub fn is_parse_error(&self) -> bool {
94        matches!(self, SerperError::Json(_))
95    }
96
97    /// Checks if the error is an API error
98    pub fn is_api_error(&self) -> bool {
99        matches!(self, SerperError::Api { .. })
100    }
101}
102
103/// Type alias for Results using SerperError
104pub type Result<T> = std::result::Result<T, SerperError>;
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_invalid_api_key_error_display() {
112        let error = SerperError::InvalidApiKey;
113        assert_eq!(error.to_string(), "Invalid API key");
114        assert!(error.is_auth_error());
115    }
116
117    #[test]
118    fn test_api_error_display() {
119        let error = SerperError::Api {
120            message: "Rate limit exceeded".to_string(),
121        };
122        assert_eq!(error.to_string(), "API error: Rate limit exceeded");
123        assert!(error.is_api_error());
124    }
125
126    #[test]
127    fn test_config_error() {
128        let error = SerperError::config_error("Invalid timeout");
129        match error {
130            SerperError::Config { message } => {
131                assert_eq!(message, "Invalid timeout");
132            }
133            _ => panic!("Expected Config error"),
134        }
135    }
136
137    #[test]
138    fn test_validation_error() {
139        let error = SerperError::validation_error("Empty query string");
140        match error {
141            SerperError::Validation { message } => {
142                assert_eq!(message, "Empty query string");
143            }
144            _ => panic!("Expected Validation error"),
145        }
146    }
147
148    #[test]
149    fn test_json_error_conversion() {
150        let json_error = serde_json::from_str::<i32>("invalid json");
151        match json_error {
152            Err(e) => {
153                let serper_error: SerperError = e.into();
154                assert!(serper_error.is_parse_error());
155            }
156            Ok(_) => panic!("Expected error"),
157        }
158    }
159
160    #[test]
161    fn test_error_variants() {
162        let api_key_error = SerperError::InvalidApiKey;
163        let api_error = SerperError::Api {
164            message: "test".to_string(),
165        };
166
167        // Test that we can match on error variants
168        match api_key_error {
169            SerperError::InvalidApiKey => {}
170            _ => panic!("Expected InvalidApiKey variant"),
171        }
172
173        match api_error {
174            SerperError::Api { message } => {
175                assert_eq!(message, "test");
176            }
177            _ => panic!("Expected Api variant"),
178        }
179    }
180
181    #[test]
182    #[allow(clippy::unnecessary_literal_unwrap)]
183    fn test_result_type_alias() {
184        let success_result: Result<i32> = Ok(42);
185        assert_eq!(success_result.unwrap(), 42);
186
187        let error_result: Result<i32> = Err(SerperError::InvalidApiKey);
188        assert!(error_result.is_err());
189    }
190
191    #[test]
192    fn test_error_classification() {
193        let auth_error = SerperError::InvalidApiKey;
194        let parse_error = SerperError::Json(
195            serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err(),
196        );
197        let api_error = SerperError::api_error("Not found");
198
199        assert!(auth_error.is_auth_error());
200        assert!(parse_error.is_parse_error());
201        assert!(api_error.is_api_error());
202    }
203}