rvoip_client_core/
call.rs

1//! Call management for SIP client
2//!
3//! This module provides call information structures and lightweight call tracking.
4//! All actual SIP/media operations are delegated to session-core.
5//!
6//! # Architecture
7//!
8//! PROPER LAYER SEPARATION:
9//! client-core -> session-core -> {transaction-core, media-core, sip-transport, sip-core}
10//!
11//! # Key Components
12//!
13//! - **CallId** - Unique identifier for each call
14//! - **CallState** - Current state in the call lifecycle  
15//! - **CallDirection** - Whether call is incoming or outgoing
16//! - **CallInfo** - Comprehensive call metadata and status
17//! - **CallStats** - Aggregate statistics about active calls
18//!
19//! # Usage Examples
20//!
21//! ## Creating Call Information
22//!
23//! ```rust
24//! use rvoip_client_core::call::{CallInfo, CallState, CallDirection, CallId};
25//! use chrono::Utc;
26//! use std::collections::HashMap;
27//!
28//! let call_info = CallInfo {
29//!     call_id: CallId::new_v4(),
30//!     state: CallState::Initiating,
31//!     direction: CallDirection::Outgoing,
32//!     local_uri: "sip:user@example.com".to_string(),
33//!     remote_uri: "sip:target@example.com".to_string(),
34//!     remote_display_name: Some("Alice Smith".to_string()),
35//!     subject: None,
36//!     created_at: Utc::now(),
37//!     connected_at: None,
38//!     ended_at: None,
39//!     remote_addr: None,
40//!     media_session_id: None,
41//!     sip_call_id: "abc123@example.com".to_string(),
42//!     metadata: HashMap::new(),
43//! };
44//!
45//! assert_eq!(call_info.state, CallState::Initiating);
46//! assert_eq!(call_info.direction, CallDirection::Outgoing);
47//! ```
48//!
49//! ## Working with Call States
50//!
51//! ```rust
52//! use rvoip_client_core::call::CallState;
53//!
54//! let state = CallState::Connected;
55//! assert!(state.is_active());
56//! assert!(state.is_in_progress());
57//! assert!(!state.is_terminated());
58//!
59//! let terminated_state = CallState::Terminated;
60//! assert!(!terminated_state.is_active());
61//! assert!(terminated_state.is_terminated());
62//! assert!(!terminated_state.is_in_progress());
63//! ```
64//!
65//! ## Checking Call Statistics
66//!
67//! ```rust
68//! use rvoip_client_core::call::CallStats;
69//!
70//! let stats = CallStats {
71//!     total_active_calls: 5,
72//!     connected_calls: 3,
73//!     incoming_pending_calls: 2,
74//! };
75//!
76//! assert_eq!(stats.total_active_calls, 5);
77//! assert_eq!(stats.connected_calls, 3);
78//! ```
79
80use serde::{Deserialize, Serialize};
81use std::collections::HashMap;
82use std::net::SocketAddr;
83use uuid::Uuid;
84use chrono::{DateTime, Utc};
85
86/// Unique identifier for a call
87/// 
88/// Each call in the system is assigned a unique UUID that remains constant
89/// throughout the call lifecycle. This ID is used to correlate events,
90/// state changes, and operations across all system components.
91/// 
92/// # Examples
93/// 
94/// ```rust
95/// use rvoip_client_core::call::CallId;
96/// use uuid::Uuid;
97/// 
98/// let call_id: CallId = Uuid::new_v4();
99/// println!("Call ID: {}", call_id);
100/// ```
101pub type CallId = Uuid;
102
103/// Current state of a call in its lifecycle
104/// 
105/// Represents the various states a call can be in, from initiation through
106/// termination. These states correspond to SIP call flow stages and help
107/// determine what operations are valid at any given time.
108/// 
109/// # State Transitions
110/// 
111/// Typical outgoing call flow:
112/// `Initiating` → `Proceeding` → `Ringing` → `Connected` → `Terminating` → `Terminated`
113/// 
114/// Typical incoming call flow:
115/// `IncomingPending` → `Connected` → `Terminating` → `Terminated`
116/// 
117/// # Examples
118/// 
119/// ```rust
120/// use rvoip_client_core::call::CallState;
121/// 
122/// let state = CallState::Connected;
123/// assert!(state.is_active());
124/// assert!(state.is_in_progress());
125/// assert!(!state.is_terminated());
126/// ```
127#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
128pub enum CallState {
129    /// Call is being initiated (sending INVITE)
130    /// 
131    /// Initial state for outgoing calls when the INVITE request is being sent
132    /// but no response has been received yet.
133    Initiating,
134    /// Received 100 Trying or similar provisional response
135    /// 
136    /// The remote party has acknowledged receipt of the INVITE and is
137    /// processing the call request.
138    Proceeding,
139    /// Received 180 Ringing
140    /// 
141    /// The remote party's phone is ringing. This indicates the call setup
142    /// is progressing normally.
143    Ringing,
144    /// Call is connected and media is flowing
145    /// 
146    /// The call has been answered and media (audio/video) can be exchanged.
147    /// This is the primary active state for established calls.
148    Connected,
149    /// Call is being terminated (sending/received BYE)
150    /// 
151    /// Either party has initiated call termination but the termination
152    /// process is not yet complete.
153    Terminating,
154    /// Call has ended normally
155    /// 
156    /// The call was successfully terminated by either party. This is a
157    /// final state.
158    Terminated,
159    /// Call failed to establish
160    /// 
161    /// The call setup failed due to network issues, busy signal, rejection,
162    /// or other errors. This is a final state.
163    Failed,
164    /// Call was cancelled before connection
165    /// 
166    /// The call was cancelled by the caller before the remote party answered.
167    /// This is a final state.
168    Cancelled,
169    /// Incoming call waiting for user decision
170    /// 
171    /// A call invitation has been received and is waiting for the user to
172    /// accept, reject, or ignore it.
173    IncomingPending,
174}
175
176impl CallState {
177    /// Check if the call is in an active state (can send/receive media)
178    /// 
179    /// Returns `true` only for the `Connected` state where media can be
180    /// actively exchanged between parties.
181    /// 
182    /// # Examples
183    /// 
184    /// ```rust
185    /// use rvoip_client_core::call::CallState;
186    /// 
187    /// assert!(CallState::Connected.is_active());
188    /// assert!(!CallState::Ringing.is_active());
189    /// assert!(!CallState::Terminated.is_active());
190    /// ```
191    pub fn is_active(&self) -> bool {
192        matches!(self, CallState::Connected)
193    }
194
195    /// Check if the call is in a terminated state
196    /// 
197    /// Returns `true` for any final state where the call has ended and
198    /// no further operations are possible.
199    /// 
200    /// # Examples
201    /// 
202    /// ```rust
203    /// use rvoip_client_core::call::CallState;
204    /// 
205    /// assert!(CallState::Terminated.is_terminated());
206    /// assert!(CallState::Failed.is_terminated());
207    /// assert!(CallState::Cancelled.is_terminated());
208    /// assert!(!CallState::Connected.is_terminated());
209    /// assert!(!CallState::Ringing.is_terminated());
210    /// ```
211    pub fn is_terminated(&self) -> bool {
212        matches!(
213            self,
214            CallState::Terminated | CallState::Failed | CallState::Cancelled
215        )
216    }
217
218    /// Check if the call is still in progress
219    /// 
220    /// Returns `true` for any non-terminal state where the call is still
221    /// active or progressing through setup/teardown.
222    /// 
223    /// # Examples
224    /// 
225    /// ```rust
226    /// use rvoip_client_core::call::CallState;
227    /// 
228    /// assert!(CallState::Connected.is_in_progress());
229    /// assert!(CallState::Ringing.is_in_progress());
230    /// assert!(CallState::Initiating.is_in_progress());
231    /// assert!(!CallState::Terminated.is_in_progress());
232    /// assert!(!CallState::Failed.is_in_progress());
233    /// ```
234    pub fn is_in_progress(&self) -> bool {
235        !self.is_terminated()
236    }
237}
238
239/// Direction of a call (from client's perspective)
240/// 
241/// Indicates whether the call was initiated by this client (outgoing) or
242/// by a remote party (incoming). This affects call handling and UI presentation.
243/// 
244/// # Examples
245/// 
246/// ```rust
247/// use rvoip_client_core::call::CallDirection;
248/// 
249/// let outgoing = CallDirection::Outgoing;
250/// let incoming = CallDirection::Incoming;
251/// 
252/// assert_eq!(outgoing, CallDirection::Outgoing);
253/// assert_ne!(outgoing, incoming);
254/// ```
255#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
256pub enum CallDirection {
257    /// Outgoing call (client initiated)
258    /// 
259    /// This client initiated the call by sending an INVITE to a remote party.
260    Outgoing,
261    /// Incoming call (received from network)
262    /// 
263    /// A remote party initiated this call by sending an INVITE to this client.
264    Incoming,
265}
266
267/// Comprehensive information about a SIP call
268/// 
269/// Contains all metadata and status information for a call, including
270/// parties involved, timing, state, and technical details.
271/// 
272/// # Examples
273/// 
274/// ```rust
275/// use rvoip_client_core::call::{CallInfo, CallState, CallDirection, CallId};
276/// use chrono::Utc;
277/// use std::collections::HashMap;
278/// 
279/// let call_info = CallInfo {
280///     call_id: CallId::new_v4(),
281///     state: CallState::Connected,
282///     direction: CallDirection::Outgoing,
283///     local_uri: "sip:alice@example.com".to_string(),
284///     remote_uri: "sip:bob@example.com".to_string(),
285///     remote_display_name: Some("Bob Smith".to_string()),
286///     subject: Some("Business call".to_string()),
287///     created_at: Utc::now(),
288///     connected_at: Some(Utc::now()),
289///     ended_at: None,
290///     remote_addr: None,
291///     media_session_id: Some("media123".to_string()),
292///     sip_call_id: "abc123@example.com".to_string(),
293///     metadata: HashMap::new(),
294/// };
295/// 
296/// assert_eq!(call_info.state, CallState::Connected);
297/// assert!(call_info.state.is_active());
298/// ```
299#[derive(Debug, Clone)]
300pub struct CallInfo {
301    /// Unique call identifier assigned by the client
302    pub call_id: CallId,
303    /// Current state of the call in its lifecycle
304    pub state: CallState,
305    /// Direction of the call (incoming or outgoing)
306    pub direction: CallDirection,
307    /// Local party URI (our user's SIP address)
308    pub local_uri: String,
309    /// Remote party URI (who we're calling or who called us)
310    pub remote_uri: String,
311    /// Display name of remote party, if provided in SIP headers
312    pub remote_display_name: Option<String>,
313    /// Call subject or reason, if provided in SIP headers
314    pub subject: Option<String>,
315    /// When the call was created/initiated
316    pub created_at: DateTime<Utc>,
317    /// When the call was answered and connected (if applicable)
318    pub connected_at: Option<DateTime<Utc>>,
319    /// When the call ended (if applicable)
320    pub ended_at: Option<DateTime<Utc>>,
321    /// Remote network address (IP and port)
322    pub remote_addr: Option<SocketAddr>,
323    /// Associated media session ID for audio/video (if any)
324    pub media_session_id: Option<String>,
325    /// SIP Call-ID header value for protocol correlation
326    pub sip_call_id: String,
327    /// Additional call metadata and custom properties
328    pub metadata: HashMap<String, String>,
329}
330
331/// Statistics about current calls in the system
332/// 
333/// Provides aggregate counts and metrics about active calls, useful for
334/// monitoring, load management, and user interface displays.
335/// 
336/// # Examples
337/// 
338/// ```rust
339/// use rvoip_client_core::call::CallStats;
340/// 
341/// let stats = CallStats {
342///     total_active_calls: 10,
343///     connected_calls: 7,
344///     incoming_pending_calls: 3,
345/// };
346/// 
347/// assert_eq!(stats.total_active_calls, 10);
348/// assert_eq!(stats.connected_calls, 7);
349/// assert_eq!(stats.incoming_pending_calls, 3);
350/// 
351/// // Calculate derived metrics
352/// let ringing_calls = stats.total_active_calls - stats.connected_calls - stats.incoming_pending_calls;
353/// assert_eq!(ringing_calls, 0);
354/// ```
355#[derive(Debug, Clone)]
356pub struct CallStats {
357    /// Total number of active calls (not in terminal states)
358    pub total_active_calls: usize,
359    /// Number of calls in Connected state (media flowing)
360    pub connected_calls: usize,
361    /// Number of incoming calls waiting for user decision
362    pub incoming_pending_calls: usize,
363}