rvoip_client_core/error.rs
1//! Error types and handling for the client-core library
2//!
3//! This module defines all error types that can occur during client operations
4//! and provides guidance on how to handle them.
5//!
6//! # Error Categories
7//!
8//! Errors are categorized to help with recovery strategies:
9//!
10//! - **Configuration Errors** - Invalid settings, can't recover without fixing config
11//! - **Network Errors** - Temporary network issues, usually recoverable with retry
12//! - **Protocol Errors** - SIP protocol violations, may need different approach
13//! - **Media Errors** - Audio/RTP issues, might need codec renegotiation
14//! - **State Errors** - Invalid operation for current state, check state first
15//!
16//! # Error Handling Guide
17//!
18//! ## Basic Pattern
19//!
20//! ```rust
21//! # use rvoip_client_core::{Client, ClientError};
22//! # use std::sync::Arc;
23//! # async fn example(client: Arc<Client>) {
24//! match client.make_call(
25//! "sip:alice@example.com".to_string(),
26//! "sip:bob@example.com".to_string(),
27//! None
28//! ).await {
29//! Ok(call_id) => {
30//! println!("Call started: {}", call_id);
31//! }
32//! Err(ClientError::NetworkError { reason }) => {
33//! eprintln!("Network problem: {}", reason);
34//! // Retry after checking network connectivity
35//! }
36//! Err(ClientError::InvalidConfiguration { field, reason }) => {
37//! eprintln!("Config error in {}: {}", field, reason);
38//! // Fix configuration before retrying
39//! }
40//! Err(e) => {
41//! eprintln!("Unexpected error: {}", e);
42//! // Log and notify user
43//! }
44//! }
45//! # }
46//! ```
47//!
48//! ## Recovery Strategies
49//!
50//! ### Network Errors
51//!
52//! Network errors are often temporary. Implement exponential backoff:
53//!
54//! ```rust
55//! # use rvoip_client_core::{Client, ClientError};
56//! # use std::sync::Arc;
57//! # use std::time::Duration;
58//! # async fn example(client: Arc<Client>) -> Result<(), Box<dyn std::error::Error>> {
59//! async fn with_retry<T, F, Fut>(
60//! mut operation: F,
61//! max_attempts: u32,
62//! ) -> Result<T, ClientError>
63//! where
64//! F: FnMut() -> Fut,
65//! Fut: std::future::Future<Output = Result<T, ClientError>>,
66//! {
67//! let mut attempt = 0;
68//! let mut delay = Duration::from_millis(100);
69//!
70//! loop {
71//! match operation().await {
72//! Ok(result) => return Ok(result),
73//! Err(e) if e.is_recoverable() && attempt < max_attempts => {
74//! attempt += 1;
75//! tokio::time::sleep(delay).await;
76//! delay *= 2; // Exponential backoff
77//! }
78//! Err(e) => return Err(e),
79//! }
80//! }
81//! }
82//!
83//! // Use with any operation
84//! let call_id = with_retry(|| async {
85//! client.make_call(
86//! "sip:alice@example.com".to_string(),
87//! "sip:bob@example.com".to_string(),
88//! None
89//! ).await
90//! }, 3).await?;
91//! # Ok(())
92//! # }
93//! ```
94//!
95//! ### Registration Errors
96//!
97//! Handle authentication and server errors:
98//!
99//! ```rust
100//! # use rvoip_client_core::{Client, ClientError};
101//! # use std::sync::Arc;
102//! # use std::time::Duration;
103//! # async fn example(client: Arc<Client>) -> Result<(), Box<dyn std::error::Error>> {
104//! match client.register_simple(
105//! "sip:alice@example.com",
106//! &"127.0.0.1:5060".parse().unwrap(),
107//! Duration::from_secs(3600)
108//! ).await {
109//! Ok(()) => {
110//! println!("Registered successfully");
111//! }
112//! Err(e) if e.is_auth_error() => {
113//! // Prompt user for credentials
114//! println!("Please check your username and password");
115//! }
116//! Err(ClientError::RegistrationFailed { reason }) => {
117//! if reason.contains("timeout") {
118//! // Server might be down
119//! println!("Server not responding, try again later");
120//! } else if reason.contains("forbidden") {
121//! // Account might be disabled
122//! println!("Registration forbidden, contact support");
123//! }
124//! }
125//! Err(e) => eprintln!("Registration error: {}", e),
126//! }
127//! # Ok(())
128//! # }
129//! ```
130//!
131//! ### Call State Errors
132//!
133//! Check state before operations:
134//!
135//! ```rust
136//! # use rvoip_client_core::{Client, ClientError, CallId};
137//! # use std::sync::Arc;
138//! # async fn example(client: Arc<Client>, call_id: CallId) -> Result<(), Box<dyn std::error::Error>> {
139//! // Safe call control with state checking
140//! async fn safe_hold_call(client: &Arc<Client>, call_id: &CallId) -> Result<(), ClientError> {
141//! // Get call info first
142//! let info = client.get_call(call_id).await?;
143//!
144//! // Check if we can hold
145//! match info.state {
146//! rvoip_client_core::call::CallState::Connected => {
147//! client.hold_call(call_id).await
148//! }
149//! rvoip_client_core::call::CallState::Failed => {
150//! // Call failed, cannot hold
151//! Err(ClientError::InvalidCallStateGeneric {
152//! expected: "Connected".to_string(),
153//! actual: "Failed".to_string(),
154//! })
155//! }
156//! _ => {
157//! Err(ClientError::InvalidCallStateGeneric {
158//! expected: "Connected".to_string(),
159//! actual: format!("{:?}", info.state),
160//! })
161//! }
162//! }
163//! }
164//!
165//! safe_hold_call(&client, &call_id).await?;
166//! # Ok(())
167//! # }
168//! ```
169//!
170//! ### Media Errors
171//!
172//! Handle codec and port allocation issues:
173//!
174//! ```rust
175//! # use rvoip_client_core::{Client, ClientError, CallId};
176//! # use std::sync::Arc;
177//! # async fn example(client: Arc<Client>, call_id: CallId) -> Result<(), Box<dyn std::error::Error>> {
178//! match client.establish_media(&call_id, "remote.example.com:30000").await {
179//! Ok(_) => println!("Media established"),
180//! Err(ClientError::MediaNegotiationFailed { reason }) => {
181//! if reason.contains("codec") {
182//! // Try with different codec
183//! println!("Codec mismatch, trying fallback");
184//! } else if reason.contains("port") {
185//! // Port allocation failed
186//! println!("No available media ports");
187//! }
188//! }
189//! Err(e) => eprintln!("Media setup failed: {}", e),
190//! }
191//! # Ok(())
192//! # }
193//! ```
194//!
195//! ## Error Context
196//!
197//! Always log errors with context for debugging:
198//!
199//! ```rust
200//! # use rvoip_client_core::{Client, ClientError, CallId};
201//! # use std::sync::Arc;
202//! # let call_id = CallId::new_v4();
203//! # async fn example(client: Arc<Client>, call_id: CallId) {
204//! use tracing::{error, warn, info};
205//!
206//! match client.answer_call(&call_id).await {
207//! Ok(_) => info!(call_id = %call_id, "Call answered successfully"),
208//! Err(e) => {
209//! error!(
210//! call_id = %call_id,
211//! error = %e,
212//! error_type = ?e,
213//! category = e.category(),
214//! "Failed to answer call"
215//! );
216//!
217//! // Take appropriate action based on error type
218//! match e {
219//! ClientError::CallNotFound { .. } => {
220//! // Call might have been cancelled
221//! }
222//! ClientError::MediaNegotiationFailed { .. } => {
223//! // Try to recover media session
224//! }
225//! _ => {
226//! // Generic error handling
227//! }
228//! }
229//! }
230//! }
231//! # }
232//! ```
233//!
234//! ## Error Categories Helper
235//!
236//! Use the `category()` method to group errors for metrics:
237//!
238//! ```rust
239//! # use rvoip_client_core::ClientError;
240//! # use std::collections::HashMap;
241//! # let errors: Vec<ClientError> = vec![];
242//! let mut error_counts: HashMap<&'static str, usize> = HashMap::new();
243//!
244//! for error in errors {
245//! *error_counts.entry(error.category()).or_insert(0) += 1;
246//! }
247//!
248//! // Report metrics
249//! for (category, count) in error_counts {
250//! println!("{}: {} errors", category, count);
251//! }
252//! ```
253
254use thiserror::Error;
255use uuid::Uuid;
256
257/// Result type alias for client-core operations
258pub type ClientResult<T> = Result<T, ClientError>;
259
260/// Comprehensive error types for SIP client operations
261///
262/// This enum covers all possible error conditions that can occur during
263/// VoIP client operations, organized by functional area for easy handling.
264///
265/// # Error Categories
266///
267/// - **Registration Errors** - SIP server registration and authentication issues
268/// - **Call Errors** - Call lifecycle and state management issues
269/// - **Media Errors** - Audio, codecs, and RTP stream issues
270/// - **Network Errors** - Connectivity and transport problems
271/// - **Protocol Errors** - SIP protocol violations and parsing issues
272/// - **Configuration Errors** - Invalid settings and missing parameters
273/// - **System Errors** - Internal failures and resource limitations
274///
275/// # Examples
276///
277/// ```rust
278/// use rvoip_client_core::ClientError;
279///
280/// // Check error categories for appropriate handling
281/// let error = ClientError::registration_failed("Invalid credentials");
282/// assert_eq!(error.category(), "registration");
283///
284/// // Check authentication errors
285/// let auth_error = ClientError::authentication_failed("Invalid password");
286/// assert!(auth_error.is_auth_error());
287///
288/// // Create specific error types
289/// let timeout_error = ClientError::OperationTimeout { duration_ms: 5000 };
290/// assert!(timeout_error.is_recoverable());
291/// ```
292#[derive(Error, Debug, Clone)]
293pub enum ClientError {
294 /// Registration related errors
295
296 /// Registration attempt failed due to server or authentication issues
297 ///
298 /// This error occurs when the SIP REGISTER request is rejected by the server,
299 /// typically due to authentication failures, server errors, or network issues.
300 ///
301 /// # Examples
302 ///
303 /// ```rust
304 /// use rvoip_client_core::ClientError;
305 ///
306 /// let error = ClientError::RegistrationFailed {
307 /// reason: "401 Unauthorized - Invalid credentials".to_string()
308 /// };
309 ///
310 /// assert_eq!(error.category(), "registration");
311 /// ```
312 #[error("Registration failed: {reason}")]
313 RegistrationFailed {
314 /// Detailed reason for the registration failure
315 reason: String
316 },
317
318 /// Client is not currently registered with any SIP server
319 ///
320 /// This error occurs when attempting operations that require an active
321 /// registration (like making calls) when no registration is active.
322 ///
323 /// # Examples
324 ///
325 /// ```rust
326 /// use rvoip_client_core::ClientError;
327 ///
328 /// let error = ClientError::NotRegistered;
329 /// assert!(error.is_auth_error());
330 /// ```
331 #[error("Not registered with server")]
332 NotRegistered,
333
334 /// SIP registration has expired and needs to be renewed
335 ///
336 /// This error occurs when the registration lifetime has elapsed and
337 /// the server no longer considers this client registered.
338 ///
339 /// # Examples
340 ///
341 /// ```rust
342 /// use rvoip_client_core::ClientError;
343 ///
344 /// let error = ClientError::RegistrationExpired;
345 /// assert!(error.is_auth_error());
346 /// assert_eq!(error.category(), "registration");
347 /// ```
348 #[error("Registration expired")]
349 RegistrationExpired,
350
351 /// SIP digest authentication failed
352 ///
353 /// This error occurs when the server rejects the authentication credentials
354 /// provided during registration or call setup.
355 ///
356 /// # Examples
357 ///
358 /// ```rust
359 /// use rvoip_client_core::ClientError;
360 ///
361 /// let error = ClientError::AuthenticationFailed {
362 /// reason: "Wrong password for user alice".to_string()
363 /// };
364 ///
365 /// assert!(error.is_auth_error());
366 /// ```
367 #[error("Authentication failed: {reason}")]
368 AuthenticationFailed {
369 /// Specific reason for authentication failure
370 reason: String
371 },
372
373 /// Call related errors
374
375 /// Attempted to operate on a call that doesn't exist
376 ///
377 /// This error occurs when referencing a call ID that is not found
378 /// in the client's active call list.
379 ///
380 /// # Examples
381 ///
382 /// ```rust
383 /// use rvoip_client_core::ClientError;
384 /// use uuid::Uuid;
385 ///
386 /// let call_id = Uuid::new_v4();
387 /// let error = ClientError::CallNotFound { call_id };
388 ///
389 /// assert!(error.is_call_error());
390 /// assert_eq!(error.category(), "call");
391 /// ```
392 #[error("Call not found: {call_id}")]
393 CallNotFound {
394 /// The call ID that was not found
395 call_id: Uuid
396 },
397
398 /// Attempted to create a call with an ID that already exists
399 ///
400 /// This error occurs when trying to create a new call with a call ID
401 /// that is already in use by another active call.
402 ///
403 /// # Examples
404 ///
405 /// ```rust
406 /// use rvoip_client_core::ClientError;
407 /// use uuid::Uuid;
408 ///
409 /// let call_id = Uuid::new_v4();
410 /// let error = ClientError::CallAlreadyExists { call_id };
411 ///
412 /// assert!(error.is_call_error());
413 /// ```
414 #[error("Call already exists: {call_id}")]
415 CallAlreadyExists {
416 /// The call ID that already exists
417 call_id: Uuid
418 },
419
420 /// Operation is not valid for the current call state
421 ///
422 /// This error occurs when attempting an operation that is not allowed
423 /// in the call's current state (e.g., trying to answer an already connected call).
424 ///
425 /// # Examples
426 ///
427 /// ```rust
428 /// use rvoip_client_core::ClientError;
429 /// use rvoip_client_core::call::CallState;
430 /// use uuid::Uuid;
431 ///
432 /// let call_id = Uuid::new_v4();
433 /// let error = ClientError::InvalidCallState {
434 /// call_id,
435 /// current_state: CallState::Terminated
436 /// };
437 ///
438 /// assert!(error.is_call_error());
439 /// ```
440 #[error("Invalid call state for call {call_id}: current state is {current_state:?}")]
441 InvalidCallState {
442 /// The call ID that has an invalid state
443 call_id: Uuid,
444 /// The current state that prevents the operation
445 current_state: crate::call::CallState
446 },
447
448 /// Generic call state validation error with string descriptions
449 ///
450 /// This error provides a more flexible way to report call state issues
451 /// when the specific CallState enum values are not available.
452 ///
453 /// # Examples
454 ///
455 /// ```rust
456 /// use rvoip_client_core::ClientError;
457 ///
458 /// let error = ClientError::InvalidCallStateGeneric {
459 /// expected: "Connected".to_string(),
460 /// actual: "Terminated".to_string()
461 /// };
462 ///
463 /// assert!(error.is_call_error());
464 /// ```
465 #[error("Invalid call state: expected {expected}, got {actual}")]
466 InvalidCallStateGeneric {
467 /// The expected call state for the operation
468 expected: String,
469 /// The actual call state that was encountered
470 actual: String
471 },
472
473 /// Call establishment or setup failed
474 ///
475 /// This error occurs during the call setup phase when the INVITE
476 /// request fails due to network, server, or remote party issues.
477 ///
478 /// # Examples
479 ///
480 /// ```rust
481 /// use rvoip_client_core::ClientError;
482 ///
483 /// let error = ClientError::CallSetupFailed {
484 /// reason: "486 Busy Here".to_string()
485 /// };
486 ///
487 /// assert!(error.is_call_error());
488 /// ```
489 #[error("Call setup failed: {reason}")]
490 CallSetupFailed {
491 /// Specific reason for call setup failure
492 reason: String
493 },
494
495 /// Call was terminated unexpectedly
496 ///
497 /// This error occurs when a call ends due to network issues,
498 /// remote party disconnect, or other unexpected conditions.
499 ///
500 /// # Examples
501 ///
502 /// ```rust
503 /// use rvoip_client_core::ClientError;
504 ///
505 /// let error = ClientError::CallTerminated {
506 /// reason: "Network connection lost".to_string()
507 /// };
508 ///
509 /// assert!(error.is_call_error());
510 /// ```
511 #[error("Call terminated: {reason}")]
512 CallTerminated {
513 /// Reason for call termination
514 reason: String
515 },
516
517 /// Media related errors
518
519 /// SDP negotiation or media setup failed
520 ///
521 /// This error occurs when the client cannot establish media streams
522 /// due to codec mismatches, network issues, or SDP parsing problems.
523 ///
524 /// # Examples
525 ///
526 /// ```rust
527 /// use rvoip_client_core::ClientError;
528 ///
529 /// let error = ClientError::MediaNegotiationFailed {
530 /// reason: "No common codecs found".to_string()
531 /// };
532 ///
533 /// assert_eq!(error.category(), "media");
534 /// ```
535 #[error("Media negotiation failed: {reason}")]
536 MediaNegotiationFailed {
537 /// Specific reason for media negotiation failure
538 reason: String
539 },
540
541 /// General media processing error
542 ///
543 /// This error covers various media-related issues including RTP
544 /// processing problems, audio device failures, and codec errors.
545 ///
546 /// # Examples
547 ///
548 /// ```rust
549 /// use rvoip_client_core::ClientError;
550 ///
551 /// let error = ClientError::MediaError {
552 /// details: "RTP packet processing failed".to_string()
553 /// };
554 ///
555 /// assert_eq!(error.category(), "media");
556 /// ```
557 #[error("Media error: {details}")]
558 MediaError {
559 /// Detailed description of the media error
560 details: String
561 },
562
563 /// No audio codecs are compatible between endpoints
564 ///
565 /// This error occurs when the local and remote endpoints cannot
566 /// agree on a common audio codec for the call.
567 ///
568 /// # Examples
569 ///
570 /// ```rust
571 /// use rvoip_client_core::ClientError;
572 ///
573 /// let error = ClientError::NoCompatibleCodecs;
574 /// assert_eq!(error.category(), "media");
575 /// ```
576 #[error("No compatible codecs")]
577 NoCompatibleCodecs,
578
579 /// Audio device (microphone/speaker) error
580 ///
581 /// This error occurs when there are problems accessing or using
582 /// the system's audio input/output devices.
583 ///
584 /// # Examples
585 ///
586 /// ```rust
587 /// use rvoip_client_core::ClientError;
588 ///
589 /// let error = ClientError::AudioDeviceError {
590 /// reason: "Microphone permission denied".to_string()
591 /// };
592 ///
593 /// assert_eq!(error.category(), "media");
594 /// ```
595 #[error("Audio device error: {reason}")]
596 AudioDeviceError {
597 /// Specific reason for audio device failure
598 reason: String
599 },
600
601 /// Network and transport errors
602
603 /// General network connectivity or communication error
604 ///
605 /// This error occurs for various network-related issues including
606 /// DNS resolution failures, socket errors, and packet loss.
607 ///
608 /// # Examples
609 ///
610 /// ```rust
611 /// use rvoip_client_core::ClientError;
612 ///
613 /// let error = ClientError::NetworkError {
614 /// reason: "DNS resolution failed for server.example.com".to_string()
615 /// };
616 ///
617 /// assert!(error.is_recoverable());
618 /// assert_eq!(error.category(), "network");
619 /// ```
620 #[error("Network error: {reason}")]
621 NetworkError {
622 /// Specific description of the network error
623 reason: String
624 },
625
626 /// Operation timed out waiting for network response
627 ///
628 /// This error occurs when network operations (like SIP requests)
629 /// do not receive a response within the configured timeout period.
630 ///
631 /// # Examples
632 ///
633 /// ```rust
634 /// use rvoip_client_core::ClientError;
635 ///
636 /// let error = ClientError::ConnectionTimeout;
637 /// assert!(error.is_recoverable());
638 /// assert_eq!(error.category(), "network");
639 /// ```
640 #[error("Connection timeout")]
641 ConnectionTimeout,
642
643 /// Cannot reach the specified SIP server
644 ///
645 /// This error occurs when the client cannot establish a connection
646 /// to the SIP server due to network issues or incorrect server address.
647 ///
648 /// # Examples
649 ///
650 /// ```rust
651 /// use rvoip_client_core::ClientError;
652 ///
653 /// let error = ClientError::ServerUnreachable {
654 /// server: "sip.example.com:5060".to_string()
655 /// };
656 ///
657 /// assert_eq!(error.category(), "network");
658 /// ```
659 #[error("Server unreachable: {server}")]
660 ServerUnreachable {
661 /// The server address that could not be reached
662 server: String
663 },
664
665 /// Protocol errors
666
667 /// SIP protocol violation or parsing error
668 ///
669 /// This error occurs when receiving malformed SIP messages or
670 /// encountering protocol violations that prevent proper communication.
671 ///
672 /// # Examples
673 ///
674 /// ```rust
675 /// use rvoip_client_core::ClientError;
676 ///
677 /// let error = ClientError::ProtocolError {
678 /// reason: "Invalid SIP URI format".to_string()
679 /// };
680 ///
681 /// assert_eq!(error.category(), "protocol");
682 /// ```
683 #[error("SIP protocol error: {reason}")]
684 ProtocolError {
685 /// Specific description of the protocol error
686 reason: String
687 },
688
689 /// Received an invalid or malformed SIP message
690 ///
691 /// This error occurs when parsing SIP messages that do not conform
692 /// to the SIP specification or contain invalid syntax.
693 ///
694 /// # Examples
695 ///
696 /// ```rust
697 /// use rvoip_client_core::ClientError;
698 ///
699 /// let error = ClientError::InvalidSipMessage {
700 /// reason: "Missing required Via header".to_string()
701 /// };
702 ///
703 /// assert_eq!(error.category(), "protocol");
704 /// ```
705 #[error("Invalid SIP message: {reason}")]
706 InvalidSipMessage {
707 /// Specific reason why the SIP message is invalid
708 reason: String
709 },
710
711 /// SIP protocol version mismatch
712 ///
713 /// This error occurs when the client and server are using incompatible
714 /// versions of the SIP protocol.
715 ///
716 /// # Examples
717 ///
718 /// ```rust
719 /// use rvoip_client_core::ClientError;
720 ///
721 /// let error = ClientError::ProtocolVersionMismatch {
722 /// expected: "SIP/2.0".to_string(),
723 /// actual: "SIP/3.0".to_string()
724 /// };
725 ///
726 /// assert_eq!(error.category(), "protocol");
727 /// ```
728 #[error("Protocol version mismatch: expected {expected}, got {actual}")]
729 ProtocolVersionMismatch {
730 /// The expected protocol version
731 expected: String,
732 /// The actual protocol version received
733 actual: String
734 },
735
736 /// Configuration errors
737
738 /// Client configuration is invalid
739 ///
740 /// This error occurs when the client is configured with invalid
741 /// settings that prevent proper operation.
742 ///
743 /// # Examples
744 ///
745 /// ```rust
746 /// use rvoip_client_core::ClientError;
747 ///
748 /// let error = ClientError::InvalidConfiguration {
749 /// field: "sip_port".to_string(),
750 /// reason: "Port must be between 1024 and 65535".to_string()
751 /// };
752 ///
753 /// assert_eq!(error.category(), "configuration");
754 /// ```
755 #[error("Invalid configuration: {field} - {reason}")]
756 InvalidConfiguration {
757 /// The configuration field that is invalid
758 field: String,
759 /// Explanation of why the configuration is invalid
760 reason: String
761 },
762
763 /// Required configuration parameter is missing
764 ///
765 /// This error occurs when mandatory configuration values are not
766 /// provided to the client.
767 ///
768 /// # Examples
769 ///
770 /// ```rust
771 /// use rvoip_client_core::ClientError;
772 ///
773 /// let error = ClientError::MissingConfiguration {
774 /// field: "server_address".to_string()
775 /// };
776 ///
777 /// assert_eq!(error.category(), "configuration");
778 /// ```
779 #[error("Missing required configuration: {field}")]
780 MissingConfiguration {
781 /// The configuration field that is missing
782 field: String
783 },
784
785 /// Transport errors
786
787 /// Network transport layer failure
788 ///
789 /// This error occurs when the underlying transport (UDP/TCP/TLS)
790 /// encounters failures that prevent SIP communication.
791 ///
792 /// # Examples
793 ///
794 /// ```rust
795 /// use rvoip_client_core::ClientError;
796 ///
797 /// let error = ClientError::TransportFailed {
798 /// reason: "TLS handshake failed".to_string()
799 /// };
800 ///
801 /// assert!(error.is_recoverable());
802 /// assert_eq!(error.category(), "network");
803 /// ```
804 #[error("Transport failed: {reason}")]
805 TransportFailed {
806 /// Specific reason for transport failure
807 reason: String
808 },
809
810 /// Requested transport type is not available
811 ///
812 /// This error occurs when trying to use a transport protocol
813 /// that is not supported or configured in the client.
814 ///
815 /// # Examples
816 ///
817 /// ```rust
818 /// use rvoip_client_core::ClientError;
819 ///
820 /// let error = ClientError::TransportNotAvailable {
821 /// transport_type: "WSS".to_string()
822 /// };
823 ///
824 /// assert_eq!(error.category(), "network");
825 /// ```
826 #[error("Transport not available: {transport_type}")]
827 TransportNotAvailable {
828 /// The transport type that is not available
829 transport_type: String
830 },
831
832 /// Session management errors
833
834 /// Session manager internal error
835 ///
836 /// This error occurs when the session management layer encounters
837 /// internal failures that prevent proper session handling.
838 ///
839 /// # Examples
840 ///
841 /// ```rust
842 /// use rvoip_client_core::ClientError;
843 ///
844 /// let error = ClientError::SessionManagerError {
845 /// reason: "Session state corruption detected".to_string()
846 /// };
847 ///
848 /// assert_eq!(error.category(), "session");
849 /// ```
850 #[error("Session manager error: {reason}")]
851 SessionManagerError {
852 /// Specific reason for session manager failure
853 reason: String
854 },
855
856 /// Maximum number of concurrent sessions exceeded
857 ///
858 /// This error occurs when attempting to create more sessions than
859 /// the configured or system-imposed limit allows.
860 ///
861 /// # Examples
862 ///
863 /// ```rust
864 /// use rvoip_client_core::ClientError;
865 ///
866 /// let error = ClientError::TooManySessions {
867 /// limit: 10
868 /// };
869 ///
870 /// assert_eq!(error.category(), "session");
871 /// ```
872 #[error("Too many sessions: limit is {limit}")]
873 TooManySessions {
874 /// The maximum number of sessions allowed
875 limit: usize
876 },
877
878 /// Generic errors
879
880 /// Internal client error
881 ///
882 /// This error indicates an unexpected internal failure within the
883 /// client library that should not normally occur.
884 ///
885 /// # Examples
886 ///
887 /// ```rust
888 /// use rvoip_client_core::ClientError;
889 ///
890 /// let error = ClientError::InternalError {
891 /// message: "Unexpected null pointer in call handler".to_string()
892 /// };
893 ///
894 /// assert_eq!(error.category(), "system");
895 /// ```
896 #[error("Internal error: {message}")]
897 InternalError {
898 /// Description of the internal error
899 message: String
900 },
901
902 /// Operation exceeded its timeout deadline
903 ///
904 /// This error occurs when an operation takes longer than its
905 /// configured timeout period to complete.
906 ///
907 /// # Examples
908 ///
909 /// ```rust
910 /// use rvoip_client_core::ClientError;
911 ///
912 /// let error = ClientError::OperationTimeout {
913 /// duration_ms: 5000
914 /// };
915 ///
916 /// assert_eq!(error.category(), "system");
917 /// ```
918 #[error("Operation timeout after {duration_ms}ms")]
919 OperationTimeout {
920 /// The timeout duration in milliseconds
921 duration_ms: u64
922 },
923
924 /// Requested feature is not yet implemented
925 ///
926 /// This error occurs when attempting to use functionality that
927 /// is planned but not yet available in the current version.
928 ///
929 /// # Examples
930 ///
931 /// ```rust
932 /// use rvoip_client_core::ClientError;
933 ///
934 /// let error = ClientError::NotImplemented {
935 /// feature: "Video calls".to_string(),
936 /// reason: "Planned for version 2.0".to_string()
937 /// };
938 ///
939 /// assert_eq!(error.category(), "system");
940 /// ```
941 #[error("Not implemented: {feature} - {reason}")]
942 NotImplemented {
943 /// The feature that is not implemented
944 feature: String,
945 /// Explanation of implementation status
946 reason: String
947 },
948
949 /// Operation not permitted in current context
950 ///
951 /// This error occurs when the user or system lacks the necessary
952 /// permissions to perform the requested operation.
953 ///
954 /// # Examples
955 ///
956 /// ```rust
957 /// use rvoip_client_core::ClientError;
958 ///
959 /// let error = ClientError::PermissionDenied {
960 /// operation: "Access microphone".to_string()
961 /// };
962 ///
963 /// assert_eq!(error.category(), "system");
964 /// ```
965 #[error("Permission denied: {operation}")]
966 PermissionDenied {
967 /// The operation that was denied
968 operation: String
969 },
970
971 /// Required resource is not available
972 ///
973 /// This error occurs when the system cannot allocate or access
974 /// a required resource (memory, ports, devices, etc.).
975 ///
976 /// # Examples
977 ///
978 /// ```rust
979 /// use rvoip_client_core::ClientError;
980 ///
981 /// let error = ClientError::ResourceUnavailable {
982 /// resource: "RTP port range 10000-20000".to_string()
983 /// };
984 ///
985 /// assert_eq!(error.category(), "system");
986 /// ```
987 #[error("Resource unavailable: {resource}")]
988 ResourceUnavailable {
989 /// The resource that is unavailable
990 resource: String
991 },
992
993 /// Codec and media format errors
994
995 /// Audio codec is not supported
996 ///
997 /// This error occurs when attempting to use an audio codec that
998 /// is not available in the current client configuration.
999 ///
1000 /// # Examples
1001 ///
1002 /// ```rust
1003 /// use rvoip_client_core::ClientError;
1004 ///
1005 /// let error = ClientError::UnsupportedCodec {
1006 /// codec: "G.729".to_string()
1007 /// };
1008 ///
1009 /// assert_eq!(error.category(), "media");
1010 /// ```
1011 #[error("Unsupported codec: {codec}")]
1012 UnsupportedCodec {
1013 /// The codec that is not supported
1014 codec: String
1015 },
1016
1017 /// Codec processing error
1018 ///
1019 /// This error occurs when there are problems encoding or decoding
1020 /// audio using the selected codec.
1021 ///
1022 /// # Examples
1023 ///
1024 /// ```rust
1025 /// use rvoip_client_core::ClientError;
1026 ///
1027 /// let error = ClientError::CodecError {
1028 /// reason: "OPUS encoder initialization failed".to_string()
1029 /// };
1030 ///
1031 /// assert_eq!(error.category(), "media");
1032 /// ```
1033 #[error("Codec error: {reason}")]
1034 CodecError {
1035 /// Specific reason for codec error
1036 reason: String
1037 },
1038
1039 /// External service errors
1040
1041 /// External service or dependency failure
1042 ///
1043 /// This error occurs when an external service (STUN server, media relay,
1044 /// authentication service, etc.) fails or becomes unavailable.
1045 ///
1046 /// # Examples
1047 ///
1048 /// ```rust
1049 /// use rvoip_client_core::ClientError;
1050 ///
1051 /// let error = ClientError::ExternalServiceError {
1052 /// service: "STUN server".to_string(),
1053 /// reason: "stun.example.com connection refused".to_string()
1054 /// };
1055 ///
1056 /// assert_eq!(error.category(), "system");
1057 /// ```
1058 #[error("External service error: {service} - {reason}")]
1059 ExternalServiceError {
1060 /// The external service that failed
1061 service: String,
1062 /// Specific reason for the service failure
1063 reason: String
1064 },
1065}
1066
1067impl ClientError {
1068 /// Create a registration failed error
1069 pub fn registration_failed(reason: impl Into<String>) -> Self {
1070 Self::RegistrationFailed { reason: reason.into() }
1071 }
1072
1073 /// Create an authentication failed error
1074 pub fn authentication_failed(reason: impl Into<String>) -> Self {
1075 Self::AuthenticationFailed { reason: reason.into() }
1076 }
1077
1078 /// Create a call setup failed error
1079 pub fn call_setup_failed(reason: impl Into<String>) -> Self {
1080 Self::CallSetupFailed { reason: reason.into() }
1081 }
1082
1083 /// Create a media negotiation failed error
1084 pub fn media_negotiation_failed(reason: impl Into<String>) -> Self {
1085 Self::MediaNegotiationFailed { reason: reason.into() }
1086 }
1087
1088 /// Create a network error
1089 pub fn network_error(reason: impl Into<String>) -> Self {
1090 Self::NetworkError { reason: reason.into() }
1091 }
1092
1093 /// Create a protocol error
1094 pub fn protocol_error(reason: impl Into<String>) -> Self {
1095 Self::ProtocolError { reason: reason.into() }
1096 }
1097
1098 /// Create an internal error
1099 pub fn internal_error(reason: impl Into<String>) -> Self {
1100 Self::InternalError { message: reason.into() }
1101 }
1102
1103 /// Check if this error is recoverable
1104 pub fn is_recoverable(&self) -> bool {
1105 match self {
1106 // Recoverable errors
1107 ClientError::NetworkError { .. } |
1108 ClientError::ConnectionTimeout |
1109 ClientError::TransportFailed { .. } |
1110 ClientError::OperationTimeout { .. } |
1111 ClientError::ExternalServiceError { .. } => true,
1112
1113 // Non-recoverable errors
1114 ClientError::InvalidConfiguration { .. } |
1115 ClientError::MissingConfiguration { .. } |
1116 ClientError::ProtocolVersionMismatch { .. } |
1117 ClientError::PermissionDenied { .. } |
1118 ClientError::NotImplemented { .. } |
1119 ClientError::UnsupportedCodec { .. } => false,
1120
1121 // Context-dependent errors
1122 _ => false,
1123 }
1124 }
1125
1126 /// Check if error indicates authentication issue
1127 pub fn is_auth_error(&self) -> bool {
1128 matches!(
1129 self,
1130 ClientError::AuthenticationFailed { .. }
1131 | ClientError::NotRegistered
1132 | ClientError::RegistrationExpired
1133 )
1134 }
1135
1136 /// Check if error is call-related
1137 pub fn is_call_error(&self) -> bool {
1138 matches!(
1139 self,
1140 ClientError::CallNotFound { .. }
1141 | ClientError::CallAlreadyExists { .. }
1142 | ClientError::InvalidCallState { .. }
1143 | ClientError::InvalidCallStateGeneric { .. }
1144 | ClientError::CallSetupFailed { .. }
1145 | ClientError::CallTerminated { .. }
1146 )
1147 }
1148
1149 /// Get error category for metrics/logging
1150 pub fn category(&self) -> &'static str {
1151 match self {
1152 ClientError::RegistrationFailed { .. } |
1153 ClientError::NotRegistered |
1154 ClientError::RegistrationExpired |
1155 ClientError::AuthenticationFailed { .. } => "registration",
1156
1157 ClientError::CallNotFound { .. } |
1158 ClientError::CallAlreadyExists { .. } |
1159 ClientError::InvalidCallState { .. } |
1160 ClientError::InvalidCallStateGeneric { .. } |
1161 ClientError::CallSetupFailed { .. } |
1162 ClientError::CallTerminated { .. } => "call",
1163
1164 ClientError::MediaNegotiationFailed { .. } |
1165 ClientError::MediaError { .. } |
1166 ClientError::NoCompatibleCodecs |
1167 ClientError::AudioDeviceError { .. } |
1168 ClientError::UnsupportedCodec { .. } |
1169 ClientError::CodecError { .. } => "media",
1170
1171 ClientError::NetworkError { .. } |
1172 ClientError::ConnectionTimeout |
1173 ClientError::ServerUnreachable { .. } |
1174 ClientError::TransportFailed { .. } |
1175 ClientError::TransportNotAvailable { .. } => "network",
1176
1177 ClientError::ProtocolError { .. } |
1178 ClientError::InvalidSipMessage { .. } |
1179 ClientError::ProtocolVersionMismatch { .. } => "protocol",
1180
1181 ClientError::InvalidConfiguration { .. } |
1182 ClientError::MissingConfiguration { .. } => "configuration",
1183
1184 ClientError::SessionManagerError { .. } |
1185 ClientError::TooManySessions { .. } => "session",
1186
1187 ClientError::InternalError { .. } |
1188 ClientError::OperationTimeout { .. } |
1189 ClientError::NotImplemented { .. } |
1190 ClientError::PermissionDenied { .. } |
1191 ClientError::ResourceUnavailable { .. } |
1192 ClientError::ExternalServiceError { .. } => "system",
1193 }
1194 }
1195}