freeswitch_types/sofia/
channel_name.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct SofiaChannelName<'a> {
17 profile: &'a str,
18 destination: &'a str,
19}
20
21impl<'a> SofiaChannelName<'a> {
22 pub fn parse(name: &'a str) -> Option<Self> {
25 let rest = name.strip_prefix("sofia/")?;
26 let (profile, destination) = rest.split_once('/')?;
27 if profile.is_empty() {
28 return None;
29 }
30 Some(Self {
31 profile,
32 destination,
33 })
34 }
35
36 pub fn profile(&self) -> &'a str {
38 self.profile
39 }
40
41 pub fn destination(&self) -> &'a str {
44 self.destination
45 }
46
47 pub fn user(&self) -> Option<&'a str> {
51 self.destination
52 .rsplit_once('@')
53 .map(|(user, _)| user)
54 }
55
56 pub fn host(&self) -> Option<&'a str> {
61 self.destination
62 .rsplit_once('@')
63 .map(|(_, host)| host)
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn inbound_full() {
73 let n = SofiaChannelName::parse("sofia/esinet1-v4-tcp/+15550001@bcf.example.test")
74 .expect("sofia name parses");
75 assert_eq!(n.profile(), "esinet1-v4-tcp");
76 assert_eq!(n.destination(), "+15550001@bcf.example.test");
77 assert_eq!(n.user(), Some("+15550001"));
78 assert_eq!(n.host(), Some("bcf.example.test"));
79 }
80
81 #[test]
82 fn simple_user_host() {
83 let n = SofiaChannelName::parse("sofia/internal-v6/x@host").expect("sofia name parses");
84 assert_eq!(n.profile(), "internal-v6");
85 assert_eq!(n.user(), Some("x"));
86 assert_eq!(n.host(), Some("host"));
87 }
88
89 #[test]
90 fn host_with_port() {
91 let n = SofiaChannelName::parse("sofia/external/1001@10.0.0.5:5080")
92 .expect("sofia name parses");
93 assert_eq!(n.user(), Some("1001"));
94 assert_eq!(n.host(), Some("10.0.0.5:5080"));
95 }
96
97 #[test]
98 fn bare_host_inbound_no_at() {
99 let n = SofiaChannelName::parse("sofia/internal/10.0.0.5").expect("sofia name parses");
100 assert_eq!(n.profile(), "internal");
101 assert_eq!(n.destination(), "10.0.0.5");
102 assert_eq!(n.user(), None);
103 assert_eq!(n.host(), None);
104 }
105
106 #[test]
107 fn gateway_dial_no_at() {
108 let n = SofiaChannelName::parse("sofia/gw1/1002").expect("sofia name parses");
109 assert_eq!(n.profile(), "gw1");
110 assert_eq!(n.destination(), "1002");
111 assert_eq!(n.user(), None);
112 assert_eq!(n.host(), None);
113 }
114
115 #[test]
116 fn dial_tail_keeps_slashes() {
117 let n = SofiaChannelName::parse("sofia/internal/sip:a@b/extra").expect("sofia name parses");
118 assert_eq!(n.destination(), "sip:a@b/extra");
119 assert_eq!(n.user(), Some("sip:a"));
120 assert_eq!(n.host(), Some("b/extra"));
121 }
122
123 #[test]
124 fn multi_at_splits_on_last() {
125 let n = SofiaChannelName::parse("sofia/internal/a@b@c.example.test")
126 .expect("sofia name parses");
127 assert_eq!(n.user(), Some("a@b"));
128 assert_eq!(n.host(), Some("c.example.test"));
129 }
130
131 #[test]
132 fn empty_destination() {
133 let n = SofiaChannelName::parse("sofia/internal/").expect("sofia name parses");
134 assert_eq!(n.profile(), "internal");
135 assert_eq!(n.destination(), "");
136 assert_eq!(n.user(), None);
137 assert_eq!(n.host(), None);
138 }
139
140 #[test]
141 fn non_sofia_and_malformed_are_none() {
142 for name in [
143 "loopback/x",
144 "error/USER_BUSY",
145 "sofia/internal",
146 "sofia//x",
147 "sofia/",
148 "sofia",
149 "",
150 "Sofia/internal/x@h",
151 ] {
152 assert_eq!(SofiaChannelName::parse(name), None, "{name:?}");
153 }
154 }
155}