freeswitch_types/variables/loopback.rs
1//! Typed mod_loopback channel variable names and the bowout marker.
2
3sip_header::define_header_enum! {
4 tests_mod: loopback_variable_generated_tests,
5 error_type: ParseLoopbackVariableError => "unknown loopback variable",
6 /// mod_loopback channel variable names (the part after the `variable_` prefix).
7 ///
8 /// Use with [`HeaderLookup::variable()`](crate::HeaderLookup::variable) for
9 /// type-safe lookups.
10 pub enum LoopbackVariable {
11 // --- Set by mod_loopback ---
12 IsLoopback => "is_loopback",
13 LoopbackLeg => "loopback_leg",
14 LoopbackFromUuid => "loopback_from_uuid",
15 OtherLoopbackFromUuid => "other_loopback_from_uuid",
16 OtherLoopbackLegUuid => "other_loopback_leg_uuid",
17 /// Set on the *real* channel bridged to a loopback leg, not on the leg.
18 OtherLegTrueId => "other_leg_true_id",
19 /// Application named by the `loopback/app=<application>[:<args>]`
20 /// destination form, which runs instead of a dialplan lookup.
21 LoopbackApp => "loopback_app",
22 LoopbackAppArg => "loopback_app_arg",
23 /// Presence marks a bowout. See [`LoopbackResignation`].
24 LoopbackHangupCause => "loopback_hangup_cause",
25 /// The real channel that outlives the bowout.
26 LoopbackBowoutOtherUuid => "loopback_bowout_other_uuid",
27
28 // --- Set by the caller ---
29 /// Vetoes the bowout when false. Unset means enabled.
30 LoopbackBowout => "loopback_bowout",
31 /// Bows out as soon as the leg executes an application, instead of
32 /// waiting for audio to flow.
33 LoopbackBowoutOnExecute => "loopback_bowout_on_execute",
34 /// Space-separated variable names to copy onto the B leg.
35 LoopbackExport => "loopback_export",
36 LoopbackInitialCodec => "loopback_initial_codec",
37 }
38}
39
40wire_enum! {
41 /// Which mod_loopback path resigned, from the `loopback_hangup_cause` value.
42 ///
43 /// Both variants mean the same thing to a consumer: the loopback leg is
44 /// gone and the call continues elsewhere. Branch on this only to log or
45 /// to tell the two triggers apart -- never to decide whether a bowout
46 /// happened. That is [`LoopbackResignation`]'s job.
47 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48 pub enum LoopbackHangupCause {
49 /// Execute-time masquerade, triggered by `loopback_bowout_on_execute`
50 /// or by an application flagged `SAF_NO_LOOPBACK`.
51 Bowout => "bowout",
52 /// Frame-count path: both legs bridged and answered, so mod_loopback
53 /// `uuid_bridge`s the two real channels together.
54 Bridge => "bridge",
55 }
56 error ParseLoopbackHangupCauseError("loopback hangup cause");
57 tests: loopback_hangup_cause_generated_tests;
58}
59
60/// A loopback leg that resigned from a call which is still up.
61///
62/// mod_loopback takes itself out of the path once it can connect the two real
63/// channels directly, so the leg emits `CHANNEL_HANGUP_COMPLETE` for a call
64/// that is alive and connected on [`other_uuid()`](Self::other_uuid). Track
65/// that channel instead of treating the hangup as a teardown.
66///
67/// Obtained from
68/// [`HeaderLookup::loopback_resignation()`](crate::HeaderLookup::loopback_resignation),
69/// which returns `Some` whenever the marker variable is present regardless of
70/// its value -- mod_loopback writes a different token per path, and a third
71/// one would still be a resignation. [`cause()`](Self::cause) asks the separate
72/// question of which path it was.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct LoopbackResignation<'a> {
75 cause_raw: &'a str,
76 other_uuid: Option<&'a str>,
77}
78
79impl<'a> LoopbackResignation<'a> {
80 pub(crate) fn new(cause_raw: &'a str, other_uuid: Option<&'a str>) -> Self {
81 Self {
82 cause_raw,
83 other_uuid,
84 }
85 }
86
87 /// The `loopback_hangup_cause` value verbatim.
88 pub fn cause_raw(&self) -> &'a str {
89 self.cause_raw
90 }
91
92 /// Which path resigned.
93 ///
94 /// `Err` means mod_loopback used a path this crate does not know yet --
95 /// worth logging, but the resignation itself still stands.
96 pub fn cause(&self) -> Result<LoopbackHangupCause, ParseLoopbackHangupCauseError> {
97 self.cause_raw
98 .parse()
99 }
100
101 /// The real channel that outlives the loopback leg, from
102 /// `loopback_bowout_other_uuid`.
103 pub fn other_uuid(&self) -> Option<&'a str> {
104 self.other_uuid
105 }
106}