ftth_rsipstack/dialog/
mod.rs

1use crate::{Error, Result};
2use crate::rsip;
3use rsip::{
4    prelude::{HeadersExt, UntypedHeader},
5    Request, Response,
6};
7
8pub mod authenticate;
9pub mod client_dialog;
10pub mod dialog;
11pub mod dialog_layer;
12pub mod invitation;
13pub mod registration;
14pub mod server_dialog;
15
16#[cfg(test)]
17mod tests;
18
19/// SIP Dialog Identifier
20///
21/// `DialogId` uniquely identifies a SIP dialog. According to RFC 3261, a dialog is
22/// identified by the Call-ID, local tag, and remote tag.
23///
24/// # Fields
25///
26/// * `call_id` - The Call-ID header field value from SIP messages, identifying a call session
27/// * `from_tag` - The tag parameter from the From header field, identifying the dialog initiator
28/// * `to_tag` - The tag parameter from the To header field, identifying the dialog recipient
29///
30/// # Examples
31///
32/// ```rust
33/// use ftth_rsipstack::dialog::DialogId;
34///
35/// let dialog_id = DialogId {
36///     call_id: "1234567890@example.com".to_string(),
37///     from_tag: "alice-tag-123".to_string(),
38///     to_tag: "bob-tag-456".to_string(),
39/// };
40///
41/// println!("Dialog ID: {}", dialog_id);
42/// ```
43///
44/// # Notes
45///
46/// - During early dialog establishment, `to_tag` may be an empty string
47/// - Dialog ID remains constant throughout the dialog lifetime
48/// - Used for managing and routing SIP messages at the dialog layer
49#[derive(Clone, Debug)]
50pub struct DialogId {
51    pub call_id: String,
52    pub from_tag: String,
53    pub to_tag: String,
54}
55
56impl PartialEq for DialogId {
57    fn eq(&self, other: &DialogId) -> bool {
58        if self.call_id != other.call_id {
59            return false;
60        }
61        if self.from_tag == other.from_tag && self.to_tag == other.to_tag {
62            return true;
63        }
64        if self.from_tag == other.to_tag && self.to_tag == other.from_tag {
65            return true;
66        }
67        false
68    }
69}
70
71impl Eq for DialogId {}
72impl std::hash::Hash for DialogId {
73    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
74        self.call_id.hash(state);
75        if self.from_tag > self.to_tag {
76            self.from_tag.hash(state);
77            self.to_tag.hash(state);
78        } else {
79            self.to_tag.hash(state);
80            self.from_tag.hash(state);
81        }
82    }
83}
84
85impl TryFrom<&Request> for DialogId {
86    type Error = crate::Error;
87
88    fn try_from(request: &Request) -> Result<Self> {
89        let call_id = request.call_id_header()?.value().to_string();
90
91        let from_tag = match request.from_header()?.tag()? {
92            Some(tag) => tag.value().to_string(),
93            None => return Err(Error::Error("from tag not found".to_string())),
94        };
95
96        let to_tag = match request.to_header()?.tag()? {
97            Some(tag) => tag.value().to_string(),
98            None => "".to_string(),
99        };
100
101        Ok(DialogId {
102            call_id,
103            from_tag,
104            to_tag,
105        })
106    }
107}
108
109impl TryFrom<&Response> for DialogId {
110    type Error = crate::Error;
111
112    fn try_from(resp: &Response) -> Result<Self> {
113        let call_id = resp.call_id_header()?.value().to_string();
114
115        let from_tag = match resp.from_header()?.tag()? {
116            Some(tag) => tag.value().to_string(),
117            None => return Err(Error::Error("from tag not found".to_string())),
118        };
119
120        let to_tag = match resp.to_header()?.tag()? {
121            Some(tag) => tag.value().to_string(),
122            None => return Err(Error::Error("to tag not found".to_string())),
123        };
124
125        Ok(DialogId {
126            call_id,
127            from_tag,
128            to_tag,
129        })
130    }
131}
132
133impl std::fmt::Display for DialogId {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        if self.from_tag > self.to_tag {
136            write!(f, "{}-{}-{}", self.call_id, self.from_tag, self.to_tag)
137        } else {
138            write!(f, "{}-{}-{}", self.call_id, self.to_tag, self.from_tag)
139        }
140    }
141}