Skip to main content

freeswitch_types/commands/endpoint/
user.rs

1use std::fmt;
2use std::str::FromStr;
3
4use super::{extract_variables, 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, uri) = extract_variables(s)?;
66        let rest = uri
67            .strip_prefix("user/")
68            .ok_or_else(|| OriginateError::ParseError("not a user endpoint".into()))?;
69        let (name, domain) = if let Some((n, d)) = rest.split_once('@') {
70            (n.to_string(), Some(d.to_string()))
71        } else {
72            (rest.to_string(), None)
73        };
74        Ok(Self {
75            name,
76            domain,
77            variables,
78        })
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn user_endpoint_display() {
88        let ep = UserEndpoint {
89            name: "1000".into(),
90            domain: Some("domain.com".into()),
91            variables: None,
92        };
93        assert_eq!(ep.to_string(), "user/1000@domain.com");
94    }
95
96    #[test]
97    fn user_endpoint_display_no_domain() {
98        let ep = UserEndpoint {
99            name: "1000".into(),
100            domain: None,
101            variables: None,
102        };
103        assert_eq!(ep.to_string(), "user/1000");
104    }
105
106    #[test]
107    fn user_endpoint_from_str() {
108        let ep: UserEndpoint = "user/1000@domain.com"
109            .parse()
110            .unwrap();
111        assert_eq!(ep.name, "1000");
112        assert_eq!(
113            ep.domain
114                .as_deref(),
115            Some("domain.com")
116        );
117    }
118
119    #[test]
120    fn user_endpoint_from_str_no_domain() {
121        let ep: UserEndpoint = "user/1000"
122            .parse()
123            .unwrap();
124        assert_eq!(ep.name, "1000");
125        assert!(ep
126            .domain
127            .is_none());
128    }
129
130    #[test]
131    fn user_endpoint_round_trip() {
132        let ep = UserEndpoint {
133            name: "bob".into(),
134            domain: Some("example.com".into()),
135            variables: None,
136        };
137        let s = ep.to_string();
138        let parsed: UserEndpoint = s
139            .parse()
140            .unwrap();
141        assert_eq!(parsed, ep);
142    }
143
144    #[test]
145    fn serde_user_endpoint() {
146        let ep = UserEndpoint {
147            name: "1000".into(),
148            domain: Some("domain.com".into()),
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
156    #[test]
157    fn serde_user_endpoint_no_domain() {
158        let ep = UserEndpoint {
159            name: "1000".into(),
160            domain: None,
161            variables: None,
162        };
163        let json = serde_json::to_string(&ep).unwrap();
164        let parsed: UserEndpoint = serde_json::from_str(&json).unwrap();
165        assert_eq!(parsed, ep);
166    }
167}