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