1#[derive(Debug)]
2pub struct QueueNameComponents<'a> {
3 pub campaign: Option<&'a str>,
4 pub tenant: Option<&'a str>,
5 pub domain: &'a str,
6 pub routing_domain: Option<&'a str>,
7}
8
9fn crack_domain(domain: &str) -> (&str, Option<&str>) {
10 match domain.split_once('!') {
11 Some((domain, routing_domain)) => (domain, Some(routing_domain)),
12 None => (domain, None),
13 }
14}
15
16impl<'a> QueueNameComponents<'a> {
17 pub fn parse(name: &'a str) -> Self {
18 match name.split_once('@') {
19 Some((prefix, domain)) => match prefix.split_once(':') {
20 Some((campaign, tenant)) => {
21 let (domain, routing_domain) = crack_domain(domain);
22 Self {
23 campaign: Some(campaign),
24 tenant: Some(tenant),
25 domain,
26 routing_domain,
27 }
28 }
29 None => {
30 let (domain, routing_domain) = crack_domain(domain);
31 Self {
32 campaign: None,
33 tenant: Some(prefix),
34 domain,
35 routing_domain,
36 }
37 }
38 },
39 None => {
40 let (domain, routing_domain) = crack_domain(name);
41 Self {
42 campaign: None,
43 tenant: None,
44 domain,
45 routing_domain,
46 }
47 }
48 }
49 }
50
51 pub fn to_string(&self) -> String {
52 Self::format(self.campaign, self.tenant, self.domain, self.routing_domain)
53 }
54
55 pub fn format<C: AsRef<str>, T: AsRef<str>, D: AsRef<str>, RD: AsRef<str>>(
56 campaign: Option<C>,
57 tenant: Option<T>,
58 domain: D,
59 routing_domain: Option<RD>,
60 ) -> String {
61 let campaign: Option<&str> = campaign.as_ref().map(|c| c.as_ref());
62 let tenant: Option<&str> = tenant.as_ref().map(|c| c.as_ref());
63 let routing_domain: Option<String> =
64 routing_domain.as_ref().map(|c| format!("!{}", c.as_ref()));
65 let routing_domain = routing_domain.as_deref().unwrap_or("");
66 let domain: &str = domain.as_ref();
67 match (campaign, tenant) {
68 (Some(c), Some(t)) => format!("{c}:{t}@{domain}{routing_domain}"),
69 (Some(c), None) => format!("{c}:@{domain}{routing_domain}"),
70 (None, Some(t)) => format!("{t}@{domain}{routing_domain}"),
71 (None, None) => format!("{domain}{routing_domain}"),
72 }
73 }
74}
75
76#[cfg(test)]
77mod test {
78 use super::*;
79
80 #[test]
81 fn test_routing_domain_syntax() {
82 k9::assert_equal!(crack_domain("foo.com"), ("foo.com", None));
83 k9::assert_equal!(
84 crack_domain("foo.com!bar.com"),
85 ("foo.com", Some("bar.com"))
86 );
87 }
88
89 #[test]
90 fn test_queue_name() {
91 k9::snapshot!(
92 QueueNameComponents::parse("foo.com"),
93 r#"
94QueueNameComponents {
95 campaign: None,
96 tenant: None,
97 domain: "foo.com",
98 routing_domain: None,
99}
100"#
101 );
102 k9::snapshot!(
103 QueueNameComponents::parse("tenant@foo.com"),
104 r#"
105QueueNameComponents {
106 campaign: None,
107 tenant: Some(
108 "tenant",
109 ),
110 domain: "foo.com",
111 routing_domain: None,
112}
113"#
114 );
115 k9::snapshot!(
116 QueueNameComponents::parse("campaign:@foo.com"),
117 r#"
118QueueNameComponents {
119 campaign: Some(
120 "campaign",
121 ),
122 tenant: Some(
123 "",
124 ),
125 domain: "foo.com",
126 routing_domain: None,
127}
128"#
129 );
130 k9::snapshot!(
131 QueueNameComponents::parse("campaign:tenant@foo.com"),
132 r#"
133QueueNameComponents {
134 campaign: Some(
135 "campaign",
136 ),
137 tenant: Some(
138 "tenant",
139 ),
140 domain: "foo.com",
141 routing_domain: None,
142}
143"#
144 );
145 k9::snapshot!(
146 QueueNameComponents::parse("campaign:tenant@foo.com!routing.com"),
147 r#"
148QueueNameComponents {
149 campaign: Some(
150 "campaign",
151 ),
152 tenant: Some(
153 "tenant",
154 ),
155 domain: "foo.com",
156 routing_domain: Some(
157 "routing.com",
158 ),
159}
160"#
161 );
162 }
163}