sip_header::define_header_enum! {
tests_mod: loopback_variable_generated_tests,
error_type: ParseLoopbackVariableError => "unknown loopback variable",
pub enum LoopbackVariable {
IsLoopback => "is_loopback",
LoopbackLeg => "loopback_leg",
LoopbackFromUuid => "loopback_from_uuid",
OtherLoopbackFromUuid => "other_loopback_from_uuid",
OtherLoopbackLegUuid => "other_loopback_leg_uuid",
OtherLegTrueId => "other_leg_true_id",
LoopbackApp => "loopback_app",
LoopbackAppArg => "loopback_app_arg",
LoopbackHangupCause => "loopback_hangup_cause",
LoopbackBowoutOtherUuid => "loopback_bowout_other_uuid",
LoopbackBowout => "loopback_bowout",
LoopbackBowoutOnExecute => "loopback_bowout_on_execute",
LoopbackExport => "loopback_export",
LoopbackInitialCodec => "loopback_initial_codec",
}
}
wire_enum! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LoopbackHangupCause {
Bowout => "bowout",
Bridge => "bridge",
}
error ParseLoopbackHangupCauseError("loopback hangup cause");
tests: loopback_hangup_cause_generated_tests;
}
wire_enum! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LoopbackLeg {
A => "A",
B => "B",
}
error ParseLoopbackLegError("loopback leg");
tests: loopback_leg_generated_tests;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LoopbackChannelName<'a> {
extension: &'a str,
leg: LoopbackLeg,
}
impl<'a> LoopbackChannelName<'a> {
pub fn parse(name: &'a str) -> Option<Self> {
let rest = name.strip_prefix("loopback/")?;
let (extension, leg) = match rest.strip_suffix("-a") {
Some(extension) => (extension, LoopbackLeg::A),
None => (rest.strip_suffix("-b")?, LoopbackLeg::B),
};
if extension.is_empty() {
return None;
}
Some(Self { extension, leg })
}
pub fn extension(&self) -> &'a str {
self.extension
}
pub fn leg(&self) -> LoopbackLeg {
self.leg
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LoopbackResignation<'a> {
cause_raw: &'a str,
other_uuid: Option<&'a str>,
}
impl<'a> LoopbackResignation<'a> {
pub(crate) fn new(cause_raw: &'a str, other_uuid: Option<&'a str>) -> Self {
Self {
cause_raw,
other_uuid,
}
}
pub fn cause_raw(&self) -> &'a str {
self.cause_raw
}
pub fn cause(&self) -> Result<LoopbackHangupCause, ParseLoopbackHangupCauseError> {
self.cause_raw
.parse()
}
pub fn other_uuid(&self) -> Option<&'a str> {
self.other_uuid
}
}
#[cfg(test)]
mod channel_name_tests {
use super::*;
#[test]
fn both_legs_of_a_dialled_extension() {
let a = LoopbackChannelName::parse("loopback/9199-a").expect("loopback name parses");
assert_eq!(a.extension(), "9199");
assert_eq!(a.leg(), LoopbackLeg::A);
let b = LoopbackChannelName::parse("loopback/9199-b").expect("loopback name parses");
assert_eq!(b.extension(), "9199");
assert_eq!(b.leg(), LoopbackLeg::B);
}
#[test]
fn app_form_carries_the_application_token() {
let n = LoopbackChannelName::parse("loopback/bridge-a").expect("loopback name parses");
assert_eq!(n.extension(), "bridge");
}
#[test]
fn an_extension_may_itself_end_in_a_leg_suffix() {
let n = LoopbackChannelName::parse("loopback/park-a-b").expect("loopback name parses");
assert_eq!(n.extension(), "park-a");
assert_eq!(n.leg(), LoopbackLeg::B);
}
#[test]
fn non_loopback_and_malformed_are_none() {
for name in [
"loopback/9199",
"loopback/9199-A",
"loopback/9199-c",
"loopback/-a",
"loopback/",
"loopback",
"sofia/internal/1000@example.com",
"Loopback/9199-a",
"",
] {
assert_eq!(LoopbackChannelName::parse(name), None, "{name:?}");
}
}
#[test]
fn leg_wire_form_is_the_variable_spelling() {
assert_eq!(LoopbackLeg::A.as_str(), "A");
assert_eq!("B".parse(), Ok(LoopbackLeg::B));
assert!("a"
.parse::<LoopbackLeg>()
.is_err());
}
}