#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SofiaChannelName<'a> {
profile: &'a str,
destination: &'a str,
}
impl<'a> SofiaChannelName<'a> {
pub fn parse(name: &'a str) -> Option<Self> {
let rest = name.strip_prefix("sofia/")?;
let (profile, destination) = rest.split_once('/')?;
if profile.is_empty() {
return None;
}
Some(Self {
profile,
destination,
})
}
pub fn profile(&self) -> &'a str {
self.profile
}
pub fn destination(&self) -> &'a str {
self.destination
}
pub fn user(&self) -> Option<&'a str> {
self.destination
.rsplit_once('@')
.map(|(user, _)| user)
}
pub fn host(&self) -> Option<&'a str> {
self.destination
.rsplit_once('@')
.map(|(_, host)| host)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inbound_full() {
let n = SofiaChannelName::parse("sofia/esinet1-v4-tcp/+15550001@bcf.example.test")
.expect("sofia name parses");
assert_eq!(n.profile(), "esinet1-v4-tcp");
assert_eq!(n.destination(), "+15550001@bcf.example.test");
assert_eq!(n.user(), Some("+15550001"));
assert_eq!(n.host(), Some("bcf.example.test"));
}
#[test]
fn simple_user_host() {
let n = SofiaChannelName::parse("sofia/internal-v6/x@host").expect("sofia name parses");
assert_eq!(n.profile(), "internal-v6");
assert_eq!(n.user(), Some("x"));
assert_eq!(n.host(), Some("host"));
}
#[test]
fn host_with_port() {
let n = SofiaChannelName::parse("sofia/external/1001@10.0.0.5:5080")
.expect("sofia name parses");
assert_eq!(n.user(), Some("1001"));
assert_eq!(n.host(), Some("10.0.0.5:5080"));
}
#[test]
fn bare_host_inbound_no_at() {
let n = SofiaChannelName::parse("sofia/internal/10.0.0.5").expect("sofia name parses");
assert_eq!(n.profile(), "internal");
assert_eq!(n.destination(), "10.0.0.5");
assert_eq!(n.user(), None);
assert_eq!(n.host(), None);
}
#[test]
fn gateway_dial_no_at() {
let n = SofiaChannelName::parse("sofia/gw1/1002").expect("sofia name parses");
assert_eq!(n.profile(), "gw1");
assert_eq!(n.destination(), "1002");
assert_eq!(n.user(), None);
assert_eq!(n.host(), None);
}
#[test]
fn dial_tail_keeps_slashes() {
let n = SofiaChannelName::parse("sofia/internal/sip:a@b/extra").expect("sofia name parses");
assert_eq!(n.destination(), "sip:a@b/extra");
assert_eq!(n.user(), Some("sip:a"));
assert_eq!(n.host(), Some("b/extra"));
}
#[test]
fn multi_at_splits_on_last() {
let n = SofiaChannelName::parse("sofia/internal/a@b@c.example.test")
.expect("sofia name parses");
assert_eq!(n.user(), Some("a@b"));
assert_eq!(n.host(), Some("c.example.test"));
}
#[test]
fn empty_destination() {
let n = SofiaChannelName::parse("sofia/internal/").expect("sofia name parses");
assert_eq!(n.profile(), "internal");
assert_eq!(n.destination(), "");
assert_eq!(n.user(), None);
assert_eq!(n.host(), None);
}
#[test]
fn non_sofia_and_malformed_are_none() {
for name in [
"loopback/x",
"error/USER_BUSY",
"sofia/internal",
"sofia//x",
"sofia/",
"sofia",
"",
"Sofia/internal/x@h",
] {
assert_eq!(SofiaChannelName::parse(name), None, "{name:?}");
}
}
}