Skip to main content

freeswitch_types/commands/endpoint/
user.rs

1use std::str::FromStr;
2
3use super::strip_endpoint_prefix;
4use crate::commands::originate::OriginateError;
5use crate::commands::variables::DialStringCarrier;
6use crate::commands::variables::Variables;
7
8/// Directory-based endpoint: `user/{name}[@{domain}]`.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[non_exhaustive]
12pub struct UserEndpoint {
13    /// User name from the directory.
14    pub name: String,
15    /// Domain name (optional, uses default domain if absent).
16    #[cfg_attr(
17        feature = "serde",
18        serde(default, skip_serializing_if = "Option::is_none")
19    )]
20    pub domain: Option<String>,
21    /// Per-channel variables prepended as `{key=value}`.
22    #[cfg_attr(
23        feature = "serde",
24        serde(default, skip_serializing_if = "Option::is_none")
25    )]
26    pub variables: Option<Variables>,
27}
28
29impl UserEndpoint {
30    /// Create a new user endpoint.
31    pub fn new(name: impl Into<String>) -> Self {
32        Self {
33            name: name.into(),
34            domain: None,
35            variables: None,
36        }
37    }
38
39    /// Set the domain.
40    pub fn with_domain(mut self, domain: impl Into<String>) -> Self {
41        self.domain = Some(domain.into());
42        self
43    }
44}
45
46impl_dial_string_with_variables!(UserEndpoint, |this, f| match &this.domain {
47    Some(d) => write!(f, "user/{}@{}", this.name, d),
48    None => write!(f, "user/{}", this.name),
49});
50
51impl FromStr for UserEndpoint {
52    type Err = OriginateError;
53
54    fn from_str(s: &str) -> Result<Self, Self::Err> {
55        let (variables, rest) =
56            strip_endpoint_prefix(s, "user/", "user", DialStringCarrier::EslApi)?;
57        let (name, domain) = if let Some((n, d)) = rest.split_once('@') {
58            (n.to_string(), Some(d.to_string()))
59        } else {
60            (rest.to_string(), None)
61        };
62        Ok(Self {
63            name,
64            domain,
65            variables,
66        })
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn user_endpoint_display() {
76        let ep = UserEndpoint {
77            name: "1000".into(),
78            domain: Some("example.com".into()),
79            variables: None,
80        };
81        assert_eq!(ep.to_string(), "user/1000@example.com");
82    }
83
84    #[test]
85    fn user_endpoint_display_no_domain() {
86        let ep = UserEndpoint {
87            name: "1000".into(),
88            domain: None,
89            variables: None,
90        };
91        assert_eq!(ep.to_string(), "user/1000");
92    }
93
94    #[test]
95    fn user_endpoint_from_str() {
96        let ep: UserEndpoint = "user/1000@example.com"
97            .parse()
98            .unwrap();
99        assert_eq!(ep.name, "1000");
100        assert_eq!(
101            ep.domain
102                .as_deref(),
103            Some("example.com")
104        );
105    }
106
107    #[test]
108    fn user_endpoint_from_str_no_domain() {
109        let ep: UserEndpoint = "user/1000"
110            .parse()
111            .unwrap();
112        assert_eq!(ep.name, "1000");
113        assert!(ep
114            .domain
115            .is_none());
116    }
117
118    #[test]
119    fn user_endpoint_round_trip() {
120        let ep = UserEndpoint {
121            name: "bob".into(),
122            domain: Some("example.com".into()),
123            variables: None,
124        };
125        let s = ep.to_string();
126        let parsed: UserEndpoint = s
127            .parse()
128            .unwrap();
129        assert_eq!(parsed, ep);
130    }
131
132    #[test]
133    fn serde_user_endpoint() {
134        let ep = UserEndpoint {
135            name: "1000".into(),
136            domain: Some("example.com".into()),
137            variables: None,
138        };
139        let json = serde_json::to_string(&ep).unwrap();
140        let parsed: UserEndpoint = serde_json::from_str(&json).unwrap();
141        assert_eq!(parsed, ep);
142    }
143
144    #[test]
145    fn serde_user_endpoint_no_domain() {
146        let ep = UserEndpoint {
147            name: "1000".into(),
148            domain: None,
149            variables: None,
150        };
151        let json = serde_json::to_string(&ep).unwrap();
152        let parsed: UserEndpoint = serde_json::from_str(&json).unwrap();
153        assert_eq!(parsed, ep);
154    }
155}