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}