aliri_macros/
lib.rs

1//! # aliri_macros
2//!
3//! Macros used by the `aliri` family of crates.
4
5#![warn(
6    missing_docs,
7    unused_import_braces,
8    unused_imports,
9    unused_qualifications
10)]
11#![deny(
12    missing_debug_implementations,
13    missing_copy_implementations,
14    trivial_casts,
15    trivial_numeric_casts,
16    unsafe_code,
17    unused_must_use
18)]
19
20/// Constructs a strongly-typed wrapper around strings
21///
22/// This macro exports a line of unsafe code to manage the reinterpretation
23/// of `&str` as the new reference type.
24#[macro_export]
25macro_rules! typed_string {
26    {
27        $(#[$meta:meta])*
28        $v:vis struct $ty:ident (String);
29
30        $(#[$meta_ref:meta])*
31        $v_ref:vis struct $ty_ref:ident (str);
32    } => {
33        #[derive(Debug, Clone, Eq, PartialEq, Hash)]
34        #[repr(transparent)]
35        $(#[$meta])*
36        $v struct $ty(String);
37
38        impl $ty {
39            /// Strongly types the given `String`.
40            #[inline]
41            pub fn new<T: Into<String>>(raw: T) -> Self {
42                Self(raw.into())
43            }
44
45            /// Unwraps the underlying value.
46            #[inline]
47            pub fn into_inner(self) -> String {
48                self.0
49            }
50
51            /// Provides access to the underlying value as a string slice.
52            #[inline]
53            pub fn as_str(&self) -> &str {
54                self.0.as_str()
55            }
56        }
57
58        impl From<String> for $ty {
59            #[inline]
60            fn from(kid: String) -> Self {
61                Self::new(kid)
62            }
63        }
64
65        impl From<&'_ str> for $ty {
66            #[inline]
67            fn from(kid: &str) -> Self {
68                Self::new(String::from(kid))
69            }
70        }
71
72        impl From<&'_ $ty_ref> for $ty {
73            #[inline]
74            fn from(kid: &$ty_ref) -> Self {
75                $ty::from(kid.as_str())
76            }
77        }
78
79        impl From<$ty> for String {
80            #[inline]
81            fn from(wrapper: $ty) -> Self {
82                wrapper.0
83            }
84        }
85
86        impl ::std::borrow::Borrow<$ty_ref> for $ty {
87            #[inline]
88            fn borrow(&self) -> &$ty_ref {
89                &self
90            }
91        }
92
93        impl ::std::ops::Deref for $ty {
94            type Target = $ty_ref;
95
96            #[inline]
97            fn deref(&self) -> &Self::Target {
98                $ty_ref::from_str(self.0.as_str())
99            }
100        }
101
102        impl AsRef<$ty_ref> for $ty {
103            #[inline]
104            fn as_ref(&self) -> &$ty_ref {
105                &self
106            }
107        }
108
109        impl<'a> ::std::fmt::Display for $ty {
110            #[inline]
111            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
112                f.write_str(self.as_str())
113            }
114        }
115
116        impl ::serde::Serialize for $ty {
117            fn serialize<S: ::serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
118                <String as ::serde::Serialize>::serialize(&self.0, serializer)
119            }
120        }
121
122        impl<'de> ::serde::Deserialize<'de> for $ty {
123            fn deserialize<D: ::serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
124                let raw = <String as ::serde::Deserialize<'de>>::deserialize(deserializer)?;
125                Ok(Self::new(raw))
126            }
127        }
128
129        #[derive(Debug, Hash, PartialEq, Eq)]
130        #[repr(transparent)]
131        $(#[$meta_ref])*
132        $v_ref struct $ty_ref(str);
133
134        impl $ty_ref {
135            #[allow(unsafe_code)]
136            #[inline]
137            /// Transparently reinterprets the string slice as a strongly-typed
138            /// value.
139            pub fn from_str(raw: &str) -> &Self {
140                let ptr: *const str = raw;
141                // `$ty_ref` is a transparent wrapper around an `str`, so this
142                // transformation is safe to do.
143                unsafe {
144                    &*(ptr as *const Self)
145                }
146            }
147
148            /// Provides access to the underlying value as a string slice.
149            #[inline]
150            pub const fn as_str(&self) -> &str {
151                &self.0
152            }
153        }
154
155        impl ToOwned for $ty_ref {
156            type Owned = $ty;
157
158            #[inline]
159            fn to_owned(&self) -> Self::Owned {
160                $ty(self.0.to_owned())
161            }
162        }
163
164        impl AsRef<$ty_ref> for $ty_ref {
165            #[inline]
166            fn as_ref(&self) -> &$ty_ref {
167                self
168            }
169        }
170
171        impl PartialEq<$ty_ref> for $ty {
172            #[inline]
173            fn eq(&self, other: &$ty_ref) -> bool {
174                self.0 == &other.0
175            }
176        }
177
178        impl PartialEq<$ty> for $ty_ref {
179            #[inline]
180            fn eq(&self, other: &$ty) -> bool {
181                &self.0 == other.0
182            }
183        }
184
185        impl<'a> ::std::fmt::Display for &'a $ty_ref {
186            #[inline]
187            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
188                f.write_str(&self.0)
189            }
190        }
191
192        impl<'de: 'a, 'a> ::serde::Deserialize<'de> for &'a $ty_ref {
193            fn deserialize<D: ::serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
194                let raw = <&str as ::serde::Deserialize<'de>>::deserialize(deserializer)?;
195                Ok($ty_ref::from_str(raw))
196            }
197        }
198
199        impl<'a> ::serde::Serialize for &'a $ty_ref {
200            fn serialize<S: ::serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
201                <&str as ::serde::Serialize>::serialize(&self.as_str(), serializer)
202            }
203        }
204    }
205}
206
207#[cfg(doctest)]
208#[doc(hidden)]
209mod doctest {
210    typed_string! {
211        #[doc(hidden)]
212        pub struct Example(String);
213
214        #[doc(hidden)]
215        pub struct ExampleRef(str);
216    }
217
218    /// Verifies that `from_str` does not extend lifetimes
219    ///
220    /// ```compile_fail
221    /// use aliri_macros::doctest::ExampleRef;
222    ///
223    /// let ex_ref = {
224    ///     let data = String::from("test string");
225    ///     ExampleRef::from_str(data.as_str())
226    /// };
227    ///
228    /// println!("{}", ex_ref);
229    /// ```
230    fn ref_from_str_does_not_extend_lifetimes() -> ! {
231        loop {}
232    }
233}