jid/
parts.rs

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        // useful for use in hashmaps
118        impl Borrow<String> for $name {
119            fn borrow(&self) -> &String {
120                &self.0
121            }
122        }
123
124        // useful for use in hashmaps
125        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                // SAFETY: repr(transparent) thing can be transmuted to/from
159                // its inner.
160                unsafe { mem::transmute(s) }
161            }
162
163            /// Access the contents as [`str`] slice.
164            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    /// The [`NodePart`] is the optional part before the (optional) `@` in any
201    /// [`Jid`][crate::Jid], whether [`BareJid`][crate::BareJid] or
202    /// [`FullJid`][crate::FullJid].
203    ///
204    /// The corresponding slice type is [`NodeRef`].
205    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
206    pub struct NodePart(String) use node_check();
207
208    /// `str`-like type which conforms to the requirements of [`NodePart`].
209    ///
210    /// See [`NodePart`] for details.
211    #[cfg_attr(feature = "serde", derive(Serialize))]
212    pub struct ref NodeRef(str);
213}
214
215def_part_types! {
216    /// The [`DomainPart`] is the part between the (optional) `@` and the
217    /// (optional) `/` in any [`Jid`][crate::Jid], whether
218    /// [`BareJid`][crate::BareJid] or [`FullJid`][crate::FullJid].
219    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
220    pub struct DomainPart(String) use domain_check();
221
222    /// `str`-like type which conforms to the requirements of [`DomainPart`].
223    ///
224    /// See [`DomainPart`] for details.
225    #[cfg_attr(feature = "serde", derive(Serialize))]
226    pub struct ref DomainRef(str);
227}
228
229def_part_types! {
230    /// The [`ResourcePart`] is the optional part after the `/` in a
231    /// [`Jid`][crate::Jid]. It is mandatory in [`FullJid`][crate::FullJid].
232    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233    pub struct ResourcePart(String) use resource_check();
234
235    /// `str`-like type which conforms to the requirements of
236    /// [`ResourcePart`].
237    ///
238    /// See [`ResourcePart`] for details.
239    #[cfg_attr(feature = "serde", derive(Serialize))]
240    pub struct ref ResourceRef(str);
241}
242
243impl DomainRef {
244    /// Construct a bare JID (a JID without a resource) from this domain and
245    /// the given node (local part).
246    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    /// Construct a bare JID (a JID without a resource) from this node (the
277    /// local part) and the given domain.
278    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}