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}