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;
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    /// Set per-channel variables.
46    pub fn with_variables(mut self, variables: Variables) -> Self {
47        self.variables = Some(variables);
48        self
49    }
50}
51
52impl fmt::Display for UserEndpoint {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        write_variables(f, &self.variables)?;
55        match &self.domain {
56            Some(d) => write!(f, "user/{}@{}", self.name, d),
57            None => write!(f, "user/{}", self.name),
58        }
59    }
60}
61
62impl FromStr for UserEndpoint {
63    type Err = OriginateError;
64
65    fn from_str(s: &str) -> Result<Self, Self::Err> {
66        let (variables, rest) = strip_endpoint_prefix(s, "user/", "user")?;
67        let (name, domain) = if let Some((n, d)) = rest.split_once('@') {
68            (n.to_string(), Some(d.to_string()))
69        } else {
70            (rest.to_string(), None)
71        };
72        Ok(Self {
73            name,
74            domain,
75            variables,
76        })
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn user_endpoint_display() {
86        let ep = UserEndpoint {
87            name: "1000".into(),
88            domain: Some("example.com".into()),
89            variables: None,
90        };
91        assert_eq!(ep.to_string(), "user/1000@example.com");
92    }
93
94    #[test]
95    fn user_endpoint_display_no_domain() {
96        let ep = UserEndpoint {
97            name: "1000".into(),
98            domain: None,
99            variables: None,
100        };
101        assert_eq!(ep.to_string(), "user/1000");
102    }
103
104    #[test]
105    fn user_endpoint_from_str() {
106        let ep: UserEndpoint = "user/1000@example.com"
107            .parse()
108            .unwrap();
109        assert_eq!(ep.name, "1000");
110        assert_eq!(
111            ep.domain
112                .as_deref(),
113            Some("example.com")
114        );
115    }
116
117    #[test]
118    fn user_endpoint_from_str_no_domain() {
119        let ep: UserEndpoint = "user/1000"
120            .parse()
121            .unwrap();
122        assert_eq!(ep.name, "1000");
123        assert!(ep
124            .domain
125            .is_none());
126    }
127
128    #[test]
129    fn user_endpoint_round_trip() {
130        let ep = UserEndpoint {
131            name: "bob".into(),
132            domain: Some("example.com".into()),
133            variables: None,
134        };
135        let s = ep.to_string();
136        let parsed: UserEndpoint = s
137            .parse()
138            .unwrap();
139        assert_eq!(parsed, ep);
140    }
141
142    #[test]
143    fn serde_user_endpoint() {
144        let ep = UserEndpoint {
145            name: "1000".into(),
146            domain: Some("example.com".into()),
147            variables: None,
148        };
149        let json = serde_json::to_string(&ep).unwrap();
150        let parsed: UserEndpoint = serde_json::from_str(&json).unwrap();
151        assert_eq!(parsed, ep);
152    }
153
154    #[test]
155    fn serde_user_endpoint_no_domain() {
156        let ep = UserEndpoint {
157            name: "1000".into(),
158            domain: None,
159            variables: None,
160        };
161        let json = serde_json::to_string(&ep).unwrap();
162        let parsed: UserEndpoint = serde_json::from_str(&json).unwrap();
163        assert_eq!(parsed, ep);
164    }
165}