Skip to main content

freeswitch_types/variables/
mod.rs

1//! Channel variable types: format parsers (`ARRAY::`, SIP multipart) and typed
2//! variable name enums.
3
4mod conference;
5mod core;
6mod core_media;
7mod esl_array;
8#[cfg(feature = "esl")]
9mod esl_headers;
10mod loopback;
11mod sip_multipart;
12mod sip_passthrough;
13#[cfg(feature = "esl")]
14pub(crate) use sip_passthrough::names_outgoing_header;
15mod sofia;
16
17pub use self::core::{ChannelVariable, ParseChannelVariableError};
18pub use conference::{ConferenceVariable, ParseConferenceVariableError};
19pub use core_media::{CoreMediaVariable, ParseCoreMediaVariableError, RtpStatUnit};
20pub use esl_array::{EslArray, EslArrayError, MAX_ARRAY_ITEMS};
21#[cfg(feature = "esl")]
22pub use esl_headers::EslHeaders;
23pub use loopback::{
24    LoopbackChannelName, LoopbackHangupCause, LoopbackLeg, LoopbackResignation, LoopbackVariable,
25    ParseLoopbackHangupCauseError, ParseLoopbackLegError, ParseLoopbackVariableError,
26};
27pub use sip_multipart::{MultipartBody, MultipartBodyError, MultipartItem};
28pub use sip_passthrough::{
29    InvalidHeaderName, ParseSipPassthroughError, SipHeaderPrefix, SipPassthroughHeader,
30};
31pub use sofia::{CarriedHeader, ParseSofiaVariableError, SofiaVariable};
32
33/// Trait for typed channel variable name enums.
34///
35/// Implement this on variable name enums to use them with
36/// [`HeaderLookup::variable()`](crate::HeaderLookup::variable) and
37/// [`variable_str()`](crate::HeaderLookup::variable_str).
38/// For variables not covered by any typed enum, use `variable_str()`.
39pub trait VariableName {
40    /// Wire-format variable name (e.g. `"sip_call_id"`).
41    fn as_str(&self) -> &str;
42
43    /// Full event header name, prefixed (e.g. `"variable_sip_call_id"`).
44    ///
45    /// For callers naming a variable with no event in hand -- a config entry, a
46    /// filter, a request field. Reading one out of a store is
47    /// [`HeaderLookup::variable()`](crate::HeaderLookup::variable), which takes
48    /// the bare name.
49    fn header_name(&self) -> String {
50        crate::lookup::variable_key(self.as_str())
51    }
52}
53
54/// Forwards to the inherent `as_str()` a `define_header_enum!` enum already has.
55macro_rules! impl_variable_name {
56    ($($enum:ident),+ $(,)?) => {
57        $(
58            impl VariableName for $enum {
59                fn as_str(&self) -> &str {
60                    $enum::as_str(self)
61                }
62            }
63        )+
64    };
65}
66
67impl_variable_name!(
68    ChannelVariable,
69    SofiaVariable,
70    CoreMediaVariable,
71    LoopbackVariable,
72    ConferenceVariable,
73);
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use crate::{HeaderLookup, VARIABLE_PREFIX};
79    use sip_header::SipHeader;
80    use std::collections::HashMap;
81
82    #[test]
83    fn header_name_composes_the_wire_key() {
84        assert_eq!(
85            ChannelVariable::ReadCodec.header_name(),
86            "variable_read_codec"
87        );
88        assert_eq!(
89            LoopbackVariable::LoopbackLeg.header_name(),
90            "variable_loopback_leg"
91        );
92        assert!(ChannelVariable::ReadCodec
93            .header_name()
94            .starts_with(VARIABLE_PREFIX));
95    }
96
97    // SipPassthroughHeader's as_str borrows from self, so the provided method
98    // must stay &self-borrowing to cover it.
99    #[test]
100    fn header_name_covers_a_borrowed_as_str() {
101        let h = SipPassthroughHeader::invite(SipHeader::CallInfo);
102        assert_eq!(h.header_name(), "variable_sip_i_call_info");
103    }
104
105    // What a downstream crate defining its own variable enum gets for free.
106    #[test]
107    fn an_external_implementor_supplies_only_as_str() {
108        struct TenantVariable;
109
110        impl VariableName for TenantVariable {
111            fn as_str(&self) -> &str {
112                "tenant_id"
113            }
114        }
115
116        assert_eq!(TenantVariable.header_name(), "variable_tenant_id");
117    }
118
119    #[test]
120    fn header_name_is_the_key_a_header_store_holds() {
121        let mut map: HashMap<String, String> = HashMap::new();
122        map.insert(ChannelVariable::ReadCodec.header_name(), "PCMU".into());
123        assert_eq!(map.variable(ChannelVariable::ReadCodec), Some("PCMU"));
124    }
125}