rvoip_dialog_core/api/errors.rs
1//! API Error Types
2//!
3//! This module provides simplified error types for the dialog-core API layer,
4//! abstracting internal complexity and providing clear error categories for
5//! application developers.
6//!
7//! ## Error Categories
8//!
9//! - **Configuration**: Invalid configuration or setup parameters
10//! - **Network**: Network connectivity or transport issues
11//! - **Protocol**: SIP protocol violations or parsing errors
12//! - **Dialog**: Dialog state or lifecycle errors
13//! - **Internal**: Internal implementation errors
14//!
15//! ## Usage Examples
16//!
17//! ### Basic Error Handling
18//!
19//! ```rust,no_run
20//! use rvoip_dialog_core::api::{ApiError, ApiResult};
21//!
22//! async fn handle_api_error(result: ApiResult<String>) {
23//! match result {
24//! Ok(value) => println!("Success: {}", value),
25//! Err(ApiError::Configuration { message }) => {
26//! eprintln!("Please check your configuration: {}", message);
27//! },
28//! Err(ApiError::Network { message }) => {
29//! eprintln!("Network problem (check connectivity): {}", message);
30//! },
31//! Err(ApiError::Protocol { message }) => {
32//! eprintln!("SIP protocol issue: {}", message);
33//! },
34//! Err(ApiError::Dialog { message }) => {
35//! eprintln!("Dialog state problem: {}", message);
36//! },
37//! Err(ApiError::Internal { message }) => {
38//! eprintln!("Internal error (please report): {}", message);
39//! },
40//! }
41//! }
42//! ```
43//!
44//! ### Error Propagation
45//!
46//! ```rust,no_run
47//! use rvoip_dialog_core::api::{ApiError, ApiResult};
48//!
49//! async fn example_function() -> ApiResult<String> {
50//! // This automatically converts from internal errors
51//! let dialog = some_dialog_operation().await?;
52//! Ok(format!("Dialog created: {}", dialog))
53//! }
54//!
55//! # async fn some_dialog_operation() -> Result<String, rvoip_dialog_core::errors::DialogError> {
56//! # Ok("test".to_string())
57//! # }
58//! ```
59
60use std::fmt;
61
62use crate::errors::DialogError;
63
64/// High-level result type for API operations
65///
66/// This is the standard Result type used throughout the dialog-core API,
67/// providing simplified error handling for application developers.
68///
69/// ## Examples
70///
71/// ```rust,no_run
72/// use rvoip_dialog_core::api::{ApiResult, ApiError};
73///
74/// async fn example_function() -> ApiResult<String> {
75/// Ok("Success".to_string())
76/// }
77///
78/// # async fn usage() {
79/// match example_function().await {
80/// Ok(result) => println!("Got: {}", result),
81/// Err(ApiError::Configuration { message }) => {
82/// eprintln!("Config error: {}", message);
83/// },
84/// Err(e) => eprintln!("Other error: {}", e),
85/// }
86/// # }
87/// ```
88pub type ApiResult<T> = Result<T, ApiError>;
89
90/// Simplified error type for API consumers
91///
92/// Provides high-level error categories that applications can handle
93/// appropriately without needing to understand internal dialog-core details.
94///
95/// ## Design Principles
96///
97/// - **User-friendly**: Clear, actionable error messages
98/// - **Categorized**: Logical grouping for appropriate handling
99/// - **Abstracted**: Hides internal implementation complexity
100/// - **Consistent**: Uniform error handling across all APIs
101///
102/// ## Error Categories
103///
104/// ### Configuration Errors
105/// Issues with setup, parameters, or invalid configurations:
106/// - Invalid URIs or addresses
107/// - Missing required parameters
108/// - Incompatible configuration combinations
109///
110/// ### Network Errors
111/// Connectivity and transport-related issues:
112/// - Connection failures
113/// - Transport errors
114/// - Timeout issues
115///
116/// ### Protocol Errors
117/// SIP protocol violations and parsing errors:
118/// - Malformed SIP messages
119/// - Protocol state violations
120/// - Unsupported SIP features
121///
122/// ### Dialog Errors
123/// Dialog state and lifecycle issues:
124/// - Dialog not found
125/// - Invalid dialog state transitions
126/// - Dialog termination errors
127///
128/// ### Internal Errors
129/// Implementation or system-level errors:
130/// - Unexpected internal states
131/// - System resource issues
132/// - Programming errors
133#[derive(Debug, Clone)]
134pub enum ApiError {
135 /// Configuration error
136 ///
137 /// Indicates an issue with configuration parameters, setup, or initialization.
138 /// These errors typically require user intervention to fix the configuration.
139 ///
140 /// ## Common Causes
141 /// - Invalid URI formats
142 /// - Missing required parameters
143 /// - Incompatible configuration options
144 /// - Invalid network addresses
145 ///
146 /// ## Example Response
147 /// Review and correct the configuration parameters.
148 Configuration {
149 /// Human-readable error message
150 message: String
151 },
152
153 /// Network error
154 ///
155 /// Indicates connectivity or transport-related issues.
156 /// These errors may be transient and worth retrying.
157 ///
158 /// ## Common Causes
159 /// - Network connectivity issues
160 /// - Server unavailable
161 /// - Connection timeouts
162 /// - Transport layer failures
163 ///
164 /// ## Example Response
165 /// Check network connectivity and retry the operation.
166 Network {
167 /// Human-readable error message
168 message: String
169 },
170
171 /// SIP protocol error
172 ///
173 /// Indicates violations of the SIP protocol or parsing errors.
174 /// These errors suggest malformed messages or protocol misuse.
175 ///
176 /// ## Common Causes
177 /// - Malformed SIP messages
178 /// - Invalid SIP headers
179 /// - Protocol state violations
180 /// - Unsupported SIP extensions
181 ///
182 /// ## Example Response
183 /// Review SIP message formatting and protocol compliance.
184 Protocol {
185 /// Human-readable error message
186 message: String
187 },
188
189 /// Dialog error
190 ///
191 /// Indicates issues with dialog state, lifecycle, or operations.
192 /// These errors suggest problems with dialog management.
193 ///
194 /// ## Common Causes
195 /// - Dialog not found
196 /// - Invalid state transitions
197 /// - Dialog already terminated
198 /// - Concurrent access issues
199 ///
200 /// ## Example Response
201 /// Check dialog state and ensure proper lifecycle management.
202 Dialog {
203 /// Human-readable error message
204 message: String
205 },
206
207 /// Internal error
208 ///
209 /// Indicates unexpected internal errors or system issues.
210 /// These errors suggest bugs or system-level problems.
211 ///
212 /// ## Common Causes
213 /// - Programming errors
214 /// - System resource exhaustion
215 /// - Unexpected internal states
216 /// - Concurrency issues
217 ///
218 /// ## Example Response
219 /// These errors should be reported as potential bugs.
220 Internal {
221 /// Human-readable error message
222 message: String
223 },
224}
225
226impl fmt::Display for ApiError {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 match self {
229 ApiError::Configuration { message } => {
230 write!(f, "Configuration error: {}", message)
231 },
232 ApiError::Network { message } => {
233 write!(f, "Network error: {}", message)
234 },
235 ApiError::Protocol { message } => {
236 write!(f, "SIP protocol error: {}", message)
237 },
238 ApiError::Dialog { message } => {
239 write!(f, "Dialog error: {}", message)
240 },
241 ApiError::Internal { message } => {
242 write!(f, "Internal error: {}", message)
243 },
244 }
245 }
246}
247
248impl std::error::Error for ApiError {}
249
250impl ApiError {
251 /// Create a configuration error
252 pub fn configuration(message: impl Into<String>) -> Self {
253 Self::Configuration { message: message.into() }
254 }
255
256 /// Create a network error
257 pub fn network(message: impl Into<String>) -> Self {
258 Self::Network { message: message.into() }
259 }
260
261 /// Create a protocol error
262 pub fn protocol(message: impl Into<String>) -> Self {
263 Self::Protocol { message: message.into() }
264 }
265
266 /// Create a dialog error
267 pub fn dialog(message: impl Into<String>) -> Self {
268 Self::Dialog { message: message.into() }
269 }
270
271 /// Create an internal error
272 pub fn internal(message: impl Into<String>) -> Self {
273 Self::Internal { message: message.into() }
274 }
275}
276
277/// Convert from internal DialogError to public ApiError
278///
279/// This conversion abstracts internal error details and provides
280/// user-friendly error categories for API consumers.
281impl From<DialogError> for ApiError {
282 fn from(error: DialogError) -> Self {
283 match error {
284 // Configuration-related errors
285 DialogError::ConfigError { message, .. } => {
286 ApiError::Configuration { message }
287 },
288
289 // Network and transport errors
290 DialogError::NetworkError { message, .. } => {
291 ApiError::Network { message }
292 },
293
294 // SIP protocol errors
295 DialogError::ProtocolError { message, .. } => {
296 ApiError::Protocol { message }
297 },
298 DialogError::RoutingError { message, .. } => {
299 ApiError::Protocol { message }
300 },
301
302 // Dialog-specific errors
303 DialogError::DialogNotFound { id, .. } => {
304 ApiError::Dialog {
305 message: format!("Dialog not found: {}", id)
306 }
307 },
308 DialogError::InvalidState { expected, actual, .. } => {
309 ApiError::Dialog {
310 message: format!("Invalid dialog state: expected {}, got {}", expected, actual)
311 }
312 },
313 DialogError::DialogAlreadyExists { id, .. } => {
314 ApiError::Dialog {
315 message: format!("Dialog already exists: {}", id)
316 }
317 },
318
319 // Transaction errors (map to internal for simplicity)
320 DialogError::TransactionError { message, .. } => {
321 ApiError::Internal {
322 message: format!("Transaction error: {}", message)
323 }
324 },
325
326 // SDP and other internal errors
327 DialogError::SdpError { message, .. } => {
328 ApiError::Internal {
329 message: format!("SDP error: {}", message)
330 }
331 },
332 DialogError::InternalError { message, .. } => {
333 ApiError::Internal { message }
334 },
335 DialogError::TimeoutError { operation, .. } => {
336 ApiError::Internal {
337 message: format!("Timeout error: {}", operation)
338 }
339 },
340 }
341 }
342}
343
344/// Convert from standard io::Error to ApiError
345impl From<std::io::Error> for ApiError {
346 fn from(error: std::io::Error) -> Self {
347 ApiError::Network {
348 message: format!("I/O error: {}", error)
349 }
350 }
351}
352
353/// Convert from serialization errors to ApiError
354impl From<serde_json::Error> for ApiError {
355 fn from(error: serde_json::Error) -> Self {
356 ApiError::Configuration {
357 message: format!("Serialization error: {}", error)
358 }
359 }
360}
361
362/// Convert from address parsing errors to ApiError
363impl From<std::net::AddrParseError> for ApiError {
364 fn from(error: std::net::AddrParseError) -> Self {
365 ApiError::Configuration {
366 message: format!("Invalid address: {}", error)
367 }
368 }
369}
370
371/// Convert from URI parsing errors to ApiError
372impl From<http::uri::InvalidUri> for ApiError {
373 fn from(error: http::uri::InvalidUri) -> Self {
374 ApiError::Configuration {
375 message: format!("Invalid URI: {}", error)
376 }
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
385 fn test_error_display() {
386 let config_error = ApiError::Configuration {
387 message: "Invalid config".to_string()
388 };
389 assert_eq!(format!("{}", config_error), "Configuration error: Invalid config");
390
391 let network_error = ApiError::Network {
392 message: "Connection failed".to_string()
393 };
394 assert_eq!(format!("{}", network_error), "Network error: Connection failed");
395 }
396
397 #[test]
398 fn test_error_constructors() {
399 let error = ApiError::configuration("test config error");
400 match error {
401 ApiError::Configuration { message } => assert_eq!(message, "test config error"),
402 _ => panic!("Expected configuration error"),
403 }
404
405 let error = ApiError::network("test network error");
406 match error {
407 ApiError::Network { message } => assert_eq!(message, "test network error"),
408 _ => panic!("Expected network error"),
409 }
410 }
411
412 #[test]
413 fn test_io_error_conversion() {
414 let io_error = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "connection refused");
415 let api_error: ApiError = io_error.into();
416
417 match api_error {
418 ApiError::Network { message } => assert!(message.contains("connection refused")),
419 _ => panic!("Expected network error"),
420 }
421 }
422
423 #[test]
424 fn test_addr_parse_error_conversion() {
425 let parse_error: std::net::AddrParseError = "invalid:address".parse::<std::net::SocketAddr>().unwrap_err();
426 let api_error: ApiError = parse_error.into();
427
428 match api_error {
429 ApiError::Configuration { message } => assert!(message.contains("Invalid address")),
430 _ => panic!("Expected configuration error"),
431 }
432 }
433}