1use alloc::borrow::{Cow, ToOwned};
2use alloc::string::{String, ToString};
3use core::borrow::Borrow;
4use core::fmt;
5use core::mem;
6use core::ops::Deref;
7use core::str::FromStr;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use crate::{domain_check, node_check, resource_check};
13use crate::{BareJid, Error, Jid};
14
15macro_rules! def_part_parse_doc {
16 ($name:ident, $other:ident, $more:expr) => {
17 concat!(
18 "Parse a [`",
19 stringify!($name),
20 "`] from a `",
21 stringify!($other),
22 "`, copying its contents.\n",
23 "\n",
24 "If the given `",
25 stringify!($other),
26 "` does not conform to the restrictions imposed by `",
27 stringify!($name),
28 "`, an error is returned.\n",
29 $more,
30 )
31 };
32}
33
34macro_rules! def_part_into_inner_doc {
35 ($name:ident, $other:ident, $more:expr) => {
36 concat!(
37 "Consume the `",
38 stringify!($name),
39 "` and return the inner `",
40 stringify!($other),
41 "`.\n",
42 $more,
43 )
44 };
45}
46
47macro_rules! def_part_types {
48 (
49 $(#[$mainmeta:meta])*
50 pub struct $name:ident(String) use $check_fn:ident();
51
52 $(#[$refmeta:meta])*
53 pub struct ref $borrowed:ident(str);
54 ) => {
55 $(#[$mainmeta])*
56 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
57 #[repr(transparent)]
58 pub struct $name(pub(crate) String);
59
60 impl $name {
61 #[doc = def_part_parse_doc!($name, str, "Depending on whether the contents are changed by normalisation operations, this function either returns a copy or a reference to the original data.")]
62 #[allow(clippy::new_ret_no_self)]
63 pub fn new(s: &str) -> Result<Cow<'_, $borrowed>, Error> {
64 let part = $check_fn(s)?;
65 match part {
66 Cow::Borrowed(v) => Ok(Cow::Borrowed($borrowed::from_str_unchecked(v))),
67 Cow::Owned(v) => Ok(Cow::Owned(Self(v))),
68 }
69 }
70
71 #[doc = def_part_into_inner_doc!($name, String, "")]
72 pub fn into_inner(self) -> String {
73 self.0
74 }
75 }
76
77 impl FromStr for $name {
78 type Err = Error;
79
80 fn from_str(s: &str) -> Result<Self, Error> {
81 Ok(Self::new(s)?.into_owned())
82 }
83 }
84
85 impl fmt::Display for $name {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 <$borrowed as fmt::Display>::fmt(Borrow::<$borrowed>::borrow(self), f)
88 }
89 }
90
91 impl Deref for $name {
92 type Target = $borrowed;
93
94 fn deref(&self) -> &Self::Target {
95 Borrow::<$borrowed>::borrow(self)
96 }
97 }
98
99 impl AsRef<$borrowed> for $name {
100 fn as_ref(&self) -> &$borrowed {
101 Borrow::<$borrowed>::borrow(self)
102 }
103 }
104
105 impl AsRef<String> for $name {
106 fn as_ref(&self) -> &String {
107 &self.0
108 }
109 }
110
111 impl Borrow<$borrowed> for $name {
112 fn borrow(&self) -> &$borrowed {
113 $borrowed::from_str_unchecked(self.0.as_str())
114 }
115 }
116
117 impl Borrow<String> for $name {
119 fn borrow(&self) -> &String {
120 &self.0
121 }
122 }
123
124 impl Borrow<str> for $name {
126 fn borrow(&self) -> &str {
127 self.0.as_str()
128 }
129 }
130
131 impl<'x> TryFrom<&'x str> for $name {
132 type Error = Error;
133
134 fn try_from(s: &str) -> Result<Self, Error> {
135 Self::from_str(s)
136 }
137 }
138
139 impl From<&$borrowed> for $name {
140 fn from(other: &$borrowed) -> Self {
141 other.to_owned()
142 }
143 }
144
145 impl<'x> From<Cow<'x, $borrowed>> for $name {
146 fn from(other: Cow<'x, $borrowed>) -> Self {
147 other.into_owned()
148 }
149 }
150
151 $(#[$refmeta])*
152 #[repr(transparent)]
153 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
154 pub struct $borrowed(pub(crate) str);
155
156 impl $borrowed {
157 pub(crate) fn from_str_unchecked(s: &str) -> &Self {
158 unsafe { mem::transmute(s) }
161 }
162
163 pub fn as_str(&self) -> &str {
165 &self.0
166 }
167 }
168
169 impl Deref for $borrowed {
170 type Target = str;
171
172 fn deref(&self) -> &Self::Target {
173 &self.0
174 }
175 }
176
177 impl ToOwned for $borrowed {
178 type Owned = $name;
179
180 fn to_owned(&self) -> Self::Owned {
181 $name(self.0.to_string())
182 }
183 }
184
185 impl AsRef<str> for $borrowed {
186 fn as_ref(&self) -> &str {
187 &self.0
188 }
189 }
190
191 impl fmt::Display for $borrowed {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 write!(f, "{}", &self.0)
194 }
195 }
196 }
197}
198
199def_part_types! {
200 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
206 pub struct NodePart(String) use node_check();
207
208 #[cfg_attr(feature = "serde", derive(Serialize))]
212 pub struct ref NodeRef(str);
213}
214
215def_part_types! {
216 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
220 pub struct DomainPart(String) use domain_check();
221
222 #[cfg_attr(feature = "serde", derive(Serialize))]
226 pub struct ref DomainRef(str);
227}
228
229def_part_types! {
230 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233 pub struct ResourcePart(String) use resource_check();
234
235 #[cfg_attr(feature = "serde", derive(Serialize))]
240 pub struct ref ResourceRef(str);
241}
242
243impl DomainRef {
244 pub fn with_node(&self, node: &NodeRef) -> BareJid {
247 BareJid::from_parts(Some(node), self)
248 }
249}
250
251impl From<DomainPart> for BareJid {
252 fn from(other: DomainPart) -> Self {
253 BareJid {
254 inner: other.into(),
255 }
256 }
257}
258
259impl From<DomainPart> for Jid {
260 fn from(other: DomainPart) -> Self {
261 Jid {
262 normalized: other.0,
263 at: None,
264 slash: None,
265 }
266 }
267}
268
269impl<'x> From<&'x DomainRef> for BareJid {
270 fn from(other: &'x DomainRef) -> Self {
271 Self::from_parts(None, other)
272 }
273}
274
275impl NodeRef {
276 pub fn with_domain(&self, domain: &DomainRef) -> BareJid {
279 BareJid::from_parts(Some(self), domain)
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn nodepart_comparison() {
289 let n1 = NodePart::new("foo").unwrap();
290 let n2 = NodePart::new("bar").unwrap();
291 let n3 = NodePart::new("foo").unwrap();
292 assert_eq!(n1, n3);
293 assert_ne!(n1, n2);
294 }
295}