ruma_identifiers/
macros.rs

1/// Declares an item with a doc attribute computed by some macro expression.
2/// This allows documentation to be dynamically generated based on input.
3/// Necessary to work around <https://github.com/rust-lang/rust/issues/52607>.
4macro_rules! doc_concat {
5    ( $( #[doc = $doc:expr] $( $thing:tt )* )* ) => ( $( #[doc = $doc] $( $thing )* )* );
6}
7
8macro_rules! partial_eq_string {
9    ($id:ty $([$( $g:ident ),*])?) => {
10        partial_eq_string!(@imp $(<$($g),*>)?, $id, str);
11        partial_eq_string!(@imp $(<$($g),*>)?, $id, &str);
12        partial_eq_string!(@imp $(<$($g),*>)?, $id, String);
13        partial_eq_string!(@imp $(<$($g),*>)?, str, $id);
14        partial_eq_string!(@imp $(<$($g),*>)?, &str, $id);
15        partial_eq_string!(@imp $(<$($g),*>)?, String, $id);
16    };
17    (@imp $(<$( $g:ident ),*>)?, $l:ty, $r:ty) => {
18        impl $(<$($g),*>)? PartialEq<$r> for $l {
19            fn eq(&self, other: &$r) -> bool {
20                AsRef::<str>::as_ref(self)
21                    == AsRef::<str>::as_ref(other)
22            }
23        }
24    }
25}
26
27macro_rules! opaque_identifier_common_impls {
28    ($id:ty) => {
29        impl $id {
30            pub(super) fn from_borrowed(s: &str) -> &Self {
31                unsafe { std::mem::transmute(s) }
32            }
33
34            pub(super) fn from_owned(s: Box<str>) -> Box<Self> {
35                unsafe { Box::from_raw(Box::into_raw(s) as _) }
36            }
37
38            pub(super) fn from_rc(s: std::rc::Rc<str>) -> std::rc::Rc<Self> {
39                unsafe { std::rc::Rc::from_raw(std::rc::Rc::into_raw(s) as _) }
40            }
41
42            pub(super) fn from_arc(s: std::sync::Arc<str>) -> std::sync::Arc<Self> {
43                unsafe { std::sync::Arc::from_raw(std::sync::Arc::into_raw(s) as _) }
44            }
45
46            pub(super) fn into_owned(self: Box<Self>) -> Box<str> {
47                unsafe { Box::from_raw(Box::into_raw(self) as _) }
48            }
49
50            doc_concat! {
51                #[doc = concat!("Creates a string slice from this `", stringify!($id), "`.")]
52                pub fn as_str(&self) -> &str {
53                    &self.0
54                }
55            }
56
57            doc_concat! {
58                #[doc = concat!("Creates a byte slice from this `", stringify!($id), "`.")]
59                pub fn as_bytes(&self) -> &[u8] {
60                    self.0.as_bytes()
61                }
62            }
63        }
64
65        impl Clone for Box<$id> {
66            fn clone(&self) -> Self {
67                (**self).to_owned()
68            }
69        }
70
71        impl ToOwned for $id {
72            type Owned = Box<$id>;
73
74            fn to_owned(&self) -> Self::Owned {
75                Self::from_owned(self.0.into())
76            }
77        }
78
79        impl AsRef<str> for $id {
80            fn as_ref(&self) -> &str {
81                self.as_str()
82            }
83        }
84
85        impl AsRef<str> for Box<$id> {
86            fn as_ref(&self) -> &str {
87                self.as_str()
88            }
89        }
90
91        impl From<&$id> for Box<$id> {
92            fn from(id: &$id) -> Self {
93                id.to_owned()
94            }
95        }
96
97        impl From<&$id> for std::rc::Rc<$id> {
98            fn from(s: &$id) -> std::rc::Rc<$id> {
99                let rc = std::rc::Rc::<str>::from(s.as_str());
100                <$id>::from_rc(rc)
101            }
102        }
103
104        impl From<&$id> for std::sync::Arc<$id> {
105            fn from(s: &$id) -> std::sync::Arc<$id> {
106                let arc = std::sync::Arc::<str>::from(s.as_str());
107                <$id>::from_arc(arc)
108            }
109        }
110
111        impl PartialEq<$id> for Box<$id> {
112            fn eq(&self, other: &$id) -> bool {
113                self.as_str() == other.as_str()
114            }
115        }
116
117        impl PartialEq<&'_ $id> for Box<$id> {
118            fn eq(&self, other: &&$id) -> bool {
119                self.as_str() == other.as_str()
120            }
121        }
122
123        impl PartialEq<Box<$id>> for $id {
124            fn eq(&self, other: &Box<$id>) -> bool {
125                self.as_str() == other.as_str()
126            }
127        }
128
129        impl PartialEq<Box<$id>> for &'_ $id {
130            fn eq(&self, other: &Box<$id>) -> bool {
131                self.as_str() == other.as_str()
132            }
133        }
134
135        impl std::fmt::Debug for $id {
136            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137                <str as std::fmt::Debug>::fmt(self.as_str(), f)
138            }
139        }
140
141        impl std::fmt::Display for $id {
142            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143                write!(f, "{}", self.as_str())
144            }
145        }
146
147        #[cfg(feature = "serde")]
148        impl serde::Serialize for $id {
149            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150            where
151                S: serde::Serializer,
152            {
153                serializer.serialize_str(self.as_str())
154            }
155        }
156
157        partial_eq_string!($id);
158        partial_eq_string!(Box<$id>);
159    };
160}
161
162macro_rules! opaque_identifier {
163    ($id:ident) => {
164        opaque_identifier_common_impls!($id);
165
166        impl<'a> From<&'a str> for &'a $id {
167            fn from(s: &'a str) -> Self {
168                $id::from_borrowed(s)
169            }
170        }
171
172        impl From<&str> for Box<$id> {
173            fn from(s: &str) -> Self {
174                $id::from_owned(s.into())
175            }
176        }
177
178        impl From<Box<str>> for Box<$id> {
179            fn from(s: Box<str>) -> Self {
180                $id::from_owned(s)
181            }
182        }
183
184        impl From<String> for Box<$id> {
185            fn from(s: String) -> Self {
186                $id::from_owned(s.into())
187            }
188        }
189
190        impl From<Box<$id>> for Box<str> {
191            fn from(id: Box<$id>) -> Self {
192                id.into_owned()
193            }
194        }
195
196        impl From<Box<$id>> for String {
197            fn from(id: Box<$id>) -> Self {
198                id.into_owned().into()
199            }
200        }
201
202        #[cfg(feature = "serde")]
203        impl<'de> serde::Deserialize<'de> for Box<$id> {
204            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205            where
206                D: serde::Deserializer<'de>,
207            {
208                Box::<str>::deserialize(deserializer).map($id::from_owned)
209            }
210        }
211    };
212}
213
214macro_rules! opaque_identifier_validated {
215    ($id:ident, $validate_id:expr) => {
216        impl $id {
217            #[rustfmt::skip]
218            doc_concat! {
219                #[doc = concat!("\
220                    Try parsing a `&str` into a `Box<", stringify!($id), ">`.\n\
221                    \n\
222                    The same can also be done using `FromStr`, `TryFrom` or `TryInto`.\n\
223                    This function is simply more constrained and thus useful in generic contexts.\
224                ")]
225                pub fn parse(
226                    s: impl AsRef<str> + Into<Box<str>>,
227                ) -> Result<Box<Self>, crate::Error> {
228                    $validate_id(s.as_ref())?;
229                    Ok($id::from_owned(s.into()))
230                }
231            }
232
233            doc_concat! {
234                #[doc = concat!("Try parsing a `&str` into an `Rc<", stringify!($id), ">`.")]
235                pub fn parse_rc(
236                    s: impl AsRef<str> + Into<std::rc::Rc<str>>,
237                ) -> Result<std::rc::Rc<Self>, crate::Error> {
238                    $validate_id(s.as_ref())?;
239                    Ok($id::from_rc(s.into()))
240                }
241            }
242
243            doc_concat! {
244                #[doc = concat!("Try parsing a `&str` into an `Arc<", stringify!($id), ">`.")]
245                pub fn parse_arc(
246                    s: impl AsRef<str> + Into<std::sync::Arc<str>>,
247                ) -> Result<std::sync::Arc<Self>, crate::Error> {
248                    $validate_id(s.as_ref())?;
249                    Ok($id::from_arc(s.into()))
250                }
251            }
252        }
253
254        opaque_identifier_common_impls!($id);
255
256        impl From<Box<$id>> for String {
257            fn from(id: Box<$id>) -> Self {
258                id.into_owned().into()
259            }
260        }
261
262        #[cfg(feature = "serde")]
263        impl<'de> serde::Deserialize<'de> for Box<$id> {
264            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
265            where
266                D: serde::Deserializer<'de>,
267            {
268                use serde::de::Error;
269
270                let s = String::deserialize(deserializer)?;
271
272                match $id::parse(s) {
273                    Ok(o) => Ok(o),
274                    Err(e) => Err(D::Error::custom(e)),
275                }
276            }
277        }
278
279        impl<'a> std::convert::TryFrom<&'a str> for &'a $id {
280            type Error = crate::Error;
281
282            fn try_from(s: &'a str) -> Result<Self, Self::Error> {
283                $validate_id(s)?;
284                Ok($id::from_borrowed(s))
285            }
286        }
287
288        impl std::str::FromStr for Box<$id> {
289            type Err = crate::Error;
290
291            fn from_str(s: &str) -> Result<Self, Self::Err> {
292                $id::parse(s)
293            }
294        }
295
296        impl std::convert::TryFrom<&str> for Box<$id> {
297            type Error = crate::Error;
298
299            fn try_from(s: &str) -> Result<Self, Self::Error> {
300                $id::parse(s)
301            }
302        }
303
304        impl std::convert::TryFrom<String> for Box<$id> {
305            type Error = crate::Error;
306
307            fn try_from(s: String) -> Result<Self, Self::Error> {
308                $id::parse(s)
309            }
310        }
311    };
312}