langextract_rust/
exceptions.rs1use thiserror::Error;
7
8pub type LangExtractResult<T> = Result<T, LangExtractError>;
10
11#[derive(Error, Debug)]
16pub enum LangExtractError {
17 #[error("Configuration error: {0}")]
19 ConfigurationError(String),
20
21 #[error("Inference error: {message}")]
23 InferenceError {
24 message: String,
25 provider: Option<String>,
26 #[source]
27 source: Option<Box<dyn std::error::Error + Send + Sync>>,
28 },
29
30 #[error("Invalid input: {0}")]
32 InvalidInput(String),
33
34 #[error("Network error: {0}")]
36 NetworkError(#[from] reqwest::Error),
37
38 #[error("Parsing error: {0}")]
40 ParsingError(String),
41
42 #[error("I/O error: {0}")]
44 IoError(#[from] std::io::Error),
45
46 #[error("Serialization error: {0}")]
48 SerializationError(String),
49
50 #[error("Processing error: {0}")]
52 ProcessingError(String),
53
54 #[error("Tokenization error: {0}")]
56 TokenizationError(String),
57
58 #[error("Chunking error: {0}")]
60 ChunkingError(String),
61
62 #[error("Visualization error: {0}")]
64 VisualizationError(String),
65
66 #[error("Unexpected error: {0}")]
68 UnexpectedError(String),
69}
70
71impl LangExtractError {
72 pub fn configuration<S: Into<String>>(message: S) -> Self {
74 Self::ConfigurationError(message.into())
75 }
76
77 pub fn inference<S: Into<String>>(
79 message: S,
80 provider: Option<String>,
81 source: Option<Box<dyn std::error::Error + Send + Sync>>,
82 ) -> Self {
83 Self::InferenceError {
84 message: message.into(),
85 provider,
86 source,
87 }
88 }
89
90 pub fn inference_simple<S: Into<String>>(message: S) -> Self {
92 Self::InferenceError {
93 message: message.into(),
94 provider: None,
95 source: None,
96 }
97 }
98
99 pub fn invalid_input<S: Into<String>>(message: S) -> Self {
101 Self::InvalidInput(message.into())
102 }
103
104 pub fn parsing<S: Into<String>>(message: S) -> Self {
106 Self::ParsingError(message.into())
107 }
108
109 pub fn serialization<S: Into<String>>(message: S) -> Self {
111 Self::SerializationError(message.into())
112 }
113
114 pub fn processing<S: Into<String>>(message: S) -> Self {
116 Self::ProcessingError(message.into())
117 }
118
119 pub fn tokenization<S: Into<String>>(message: S) -> Self {
121 Self::TokenizationError(message.into())
122 }
123
124 pub fn chunking<S: Into<String>>(message: S) -> Self {
126 Self::ChunkingError(message.into())
127 }
128
129 pub fn visualization<S: Into<String>>(message: S) -> Self {
131 Self::VisualizationError(message.into())
132 }
133
134 pub fn unexpected<S: Into<String>>(message: S) -> Self {
136 Self::UnexpectedError(message.into())
137 }
138
139 pub fn provider(&self) -> Option<&str> {
141 match self {
142 Self::InferenceError { provider, .. } => provider.as_deref(),
143 _ => None,
144 }
145 }
146
147 pub fn is_configuration_error(&self) -> bool {
149 matches!(self, Self::ConfigurationError(_))
150 }
151
152 pub fn is_inference_error(&self) -> bool {
154 matches!(self, Self::InferenceError { .. })
155 }
156
157 pub fn is_network_error(&self) -> bool {
159 matches!(self, Self::NetworkError(_))
160 }
161
162 pub fn is_parsing_error(&self) -> bool {
164 matches!(self, Self::ParsingError(_))
165 }
166}
167
168impl From<serde_json::Error> for LangExtractError {
170 fn from(err: serde_json::Error) -> Self {
171 Self::SerializationError(format!("JSON error: {}", err))
172 }
173}
174
175impl From<serde_yaml::Error> for LangExtractError {
177 fn from(err: serde_yaml::Error) -> Self {
178 Self::SerializationError(format!("YAML error: {}", err))
179 }
180}
181
182impl From<dotenvy::Error> for LangExtractError {
184 fn from(err: dotenvy::Error) -> Self {
185 Self::ConfigurationError(format!("Environment variable error: {}", err))
186 }
187}
188
189#[derive(Error, Debug)]
191pub enum InferenceError {
192 #[error("No outputs available: {message}")]
194 NoOutputsAvailable { message: String },
195
196 #[error("Rate limit exceeded for provider {provider}")]
198 RateLimitExceeded { provider: String },
199
200 #[error("Invalid model configuration: {message}")]
202 InvalidConfiguration { message: String },
203
204 #[error("Model not found: {model_id}")]
206 ModelNotFound { model_id: String },
207
208 #[error("Authentication failed for provider {provider}: {message}")]
210 AuthenticationFailed { provider: String, message: String },
211
212 #[error("Quota exceeded for provider {provider}")]
214 QuotaExceeded { provider: String },
215
216 #[error("Service unavailable for provider {provider}")]
218 ServiceUnavailable { provider: String },
219
220 #[error("Inference failed: {message}")]
222 InferenceFailed { message: String },
223}
224
225impl From<InferenceError> for LangExtractError {
226 fn from(err: InferenceError) -> Self {
227 let provider = match &err {
228 InferenceError::RateLimitExceeded { provider } => Some(provider.clone()),
229 InferenceError::AuthenticationFailed { provider, .. } => Some(provider.clone()),
230 InferenceError::QuotaExceeded { provider } => Some(provider.clone()),
231 InferenceError::ServiceUnavailable { provider } => Some(provider.clone()),
232 _ => None,
233 };
234
235 Self::InferenceError {
236 message: err.to_string(),
237 provider,
238 source: Some(Box::new(err)),
239 }
240 }
241}
242
243#[derive(Error, Debug)]
245pub enum ResolverError {
246 #[error("Failed to parse output: {message}")]
248 ParseError { message: String },
249
250 #[error("Invalid output format: expected {expected}, got {actual}")]
252 InvalidFormat { expected: String, actual: String },
253
254 #[error("Missing required fields: {fields:?}")]
256 MissingFields { fields: Vec<String> },
257
258 #[error("Schema validation failed: {message}")]
260 SchemaValidationFailed { message: String },
261}
262
263impl From<ResolverError> for LangExtractError {
264 fn from(err: ResolverError) -> Self {
265 Self::ParsingError(err.to_string())
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_error_creation() {
275 let config_err = LangExtractError::configuration("Missing API key");
276 assert!(config_err.is_configuration_error());
277 assert!(!config_err.is_inference_error());
278
279 let inference_err = LangExtractError::inference(
280 "Model failed",
281 Some("gemini".to_string()),
282 None,
283 );
284 assert!(inference_err.is_inference_error());
285 assert_eq!(inference_err.provider(), Some("gemini"));
286
287 let parsing_err = LangExtractError::parsing("Invalid JSON");
288 assert!(parsing_err.is_parsing_error());
289 }
290
291 #[test]
292 fn test_error_conversion() {
293 let json_error = serde_json::from_str::<serde_json::Value>("invalid json");
294 assert!(json_error.is_err());
295
296 let lang_error: LangExtractError = json_error.unwrap_err().into();
297 assert!(lang_error.is_parsing_error() || matches!(lang_error, LangExtractError::SerializationError(_)));
298 }
299
300 #[test]
301 fn test_inference_error_conversion() {
302 let inference_err = InferenceError::ModelNotFound {
303 model_id: "unknown-model".to_string(),
304 };
305
306 let lang_err: LangExtractError = inference_err.into();
307 assert!(lang_err.is_inference_error());
308 }
309
310 #[test]
311 fn test_error_display() {
312 let error = LangExtractError::configuration("Test error message");
313 let display = format!("{}", error);
314 assert!(display.contains("Configuration error"));
315 assert!(display.contains("Test error message"));
316 }
317
318 #[test]
319 fn test_resolver_error_conversion() {
320 let resolver_err = ResolverError::ParseError {
321 message: "Invalid JSON structure".to_string(),
322 };
323
324 let lang_err: LangExtractError = resolver_err.into();
325 assert!(lang_err.is_parsing_error());
326 }
327}