Skip to main content

freeswitch_types/commands/endpoint/
user.rs

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