Skip to main content

freeswitch_types/commands/endpoint/
user.rs

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