wiwi/
nominal.rs

1//! Utilities for nominal typing
2
3use crate::prelude::*;
4
5/// Declare a new nominal type (alias), with the provided name, a name for the
6/// marker type struct, and the wrapped type
7///
8/// The returned type alias will be of a guaranteed unique type. This is done by
9/// creating a new ZST with the provided marker type struct name.
10///
11/// The name of the new marker type struct is something we hope to eventually be
12/// able to generate automatically from the given newtype name. If there is a way,
13/// we don't know how >~<
14///
15/// # Examples
16///
17/// Basic usage:
18///
19/// ```
20/// # use wiwi::nominal::{ Nominal, nominal };
21/// // type NewType = Nominal<String, NewTypeMarker>;
22/// nominal!(NewType, marker: NewTypeMarker, wraps: String);
23///
24/// // these two are identical
25/// let item: NewType = NewType::new(String::new());
26/// let item: Nominal<String, NewTypeMarker> = Nominal::new(String::new());
27///
28/// // and of course, it's a type alias
29/// let item: NewType = Nominal::new(String::new());
30/// let item: Nominal<String, NewTypeMarker> = NewType::new(String::new());
31/// ```
32///
33/// The macro does indeed create a unique newtype:
34///
35/// ```compile_fail
36/// # // TODO: use err code E0308 (currently nightly only)
37/// # use wiwi::nominal::nominal;
38/// # nominal!(NewType, marker: NewTypeMarker, wraps: String);
39/// nominal!(AnotherNewType, marker: AnotherNewTypeMarker, wraps: String);
40///
41/// let item: NewType = NewType::new(String::new());
42/// // this won't compile
43/// let another_item: NewType = AnotherNewType::new(String::new());
44/// ```
45///
46/// Controlling visibility of the type alias / marker struct:
47///
48/// ```
49/// mod inner {
50///    # use wiwi::nominal::nominal;
51///    nominal!(pub NewType, marker: NewTypeMarker, wraps: String);
52///    //   ↑
53/// }
54///
55/// let item = inner::NewType::new(String::new());
56/// ```
57///
58/// Private visibility is default (like the rest of Rust visibilities):
59///
60/// ```compile_fail
61/// # // TODO: use err code E0603 (currently nightly only)
62/// mod inner {
63///    # use wiwi::nominal::nominal;
64///    nominal!(NewType, marker: NewTypeMarker, wraps: String);
65///    //       ↑ no `pub`
66/// }
67///
68/// // this won't compile
69/// let item = inner::NewType::new(String::new());
70/// ```
71///
72/// Other visibilities work too, of course:
73///
74/// ```compile_fail
75/// # // TODO: use err code E0603 (currently nightly only)
76/// mod outer {
77///    mod inner {
78///       # // pag
79///       # use wiwi::nominal::nominal;
80///       nominal!(pub(super) NewType, marker: NewTypeMarker, wraps: String);
81///    }
82///
83///    # fn _maybe_this_fn_decl_shouldnt_be_hidden_i_dont_know() {
84///    // this is fine...
85///    let item = inner::NewType::new(String::new());
86///    # }
87/// }
88///
89/// // but this won't compile
90/// let item = outer::inner::NewType::new(String::new());
91/// ```
92#[macro_export]
93macro_rules! nominal {
94	($vis:vis $name:ident, marker: $marker:ident, wraps: $( ref <$($lifetimes:lifetime),+> )? $ty:ty) => {
95		$vis struct $marker;
96		$vis type $name$(<$($lifetimes),+>)? = $crate::nominal::Nominal<$ty, $marker>;
97	};
98}
99pub use nominal;
100
101/// Declare many new nominal types (aliases), in a module
102///
103/// Usage is more or less identical to [`nominal`], but you define a module
104/// inside the macro invocation. Because this macro creates a new module
105/// (with the name you specify), and the created module is only used for defining
106/// these
107/// nominal types, there can be nothing else in there, which we take advantage of
108/// to create a `marker` submodule to define marker types in. This way it can have
109/// a new namespace just for the marker types, so reusing the newtype name won't
110/// collide with anything else.
111///
112/// So, all of that is to say this macro also saves you a bit of boilerplate
113/// declaring names for the newtype ZSTs.
114///
115/// # Examples
116///
117/// ```
118/// # use wiwi::nominal::nominal_mod;
119/// nominal_mod! {
120///    pub mod nominal {
121///       nominal!(pub NewType, wraps: String);
122///    }
123/// }
124///
125/// let item = nominal::NewType::new(String::new());
126/// ```
127///
128/// Still creating newtypes as expected:
129///
130/// ```compile_fail
131/// # // TODO: use err code E0308 (currently nightly only)
132/// # use wiwi::nominal::nominal_mod;
133/// nominal_mod! {
134///    pub mod nominal {
135///       nominal!(pub NewType, wraps: String);
136///       nominal!(pub AnotherNewType, wraps: String);
137///    }
138/// }
139///
140/// let item: nominal::NewType = nominal::NewType::new(String::new());
141/// // this won't compile
142/// let another_item: nominal::NewType = nominal::AnotherNewType::new(String::new());
143/// ```
144///
145/// Still "just" a type alias:
146///
147/// ```
148/// # use wiwi::nominal::{ Nominal, nominal_mod };
149/// # nominal_mod! {
150/// #    pub mod nominal {
151/// #       nominal!(pub NewType, wraps: String);
152/// #    }
153/// # }
154///
155/// let item: nominal::NewType = Nominal::new(String::new());
156/// ```
157///
158/// Created marker structs are in a `marker` submodule:
159///
160/// ```
161/// # use wiwi::nominal::{ Nominal, nominal_mod };
162/// # nominal_mod! {
163/// #    pub mod nominal {
164/// #       nominal!(pub NewType, wraps: String);
165/// #       nominal!(pub AnotherNewType, wraps: String);
166/// #    }
167/// # }
168///
169/// let item: Nominal<String, nominal::marker::NewType> = nominal::NewType::new(String::new());
170/// let item: Nominal<String, nominal::marker::AnotherNewType> = nominal::AnotherNewType::new(String::new());
171/// ```
172#[macro_export]
173macro_rules! nominal_mod {
174	{
175		$(
176			$mod_vis:vis mod $mod_name:ident {
177				$( nominal!($item_vis:vis $name:ident, wraps: $( ref <$($lifetimes:lifetime),+> )? $type:ty); )*
178			}
179		)*
180	} => {
181		$(
182			$mod_vis mod $mod_name {
183				pub mod marker {
184					$( pub struct $name; )*
185				}
186
187				use super::*;
188				$( $item_vis type $name$( <$($lifetimes),+> )? = $crate::nominal::Nominal<$type, marker::$name>; )*
189			}
190		)*
191	}
192}
193pub use nominal_mod;
194
195/// Nominal wrapper struct
196///
197/// This struct is zero cost; it is simply a type safe wrapper.
198///
199/// Newtypes are primarily created with assistance from the [`nominal`] and
200/// [`nominal_mod`] macros. The macros will help save you the boilerplate of
201/// writing the types and declaring unit structs to use as the marker.
202#[repr(transparent)]
203pub struct Nominal<T, M> {
204	item: T,
205	marker: PhantomData<M>
206}
207
208impl<T, M> Nominal<T, M> {
209	/// Creates a nominal struct with the given value
210	#[inline]
211	pub fn new(item: T) -> Self {
212		Self { item, marker: PhantomData }
213	}
214
215	/// Unwraps the nominal struct and returns the value
216	#[inline]
217	pub fn into_inner(self) -> T {
218		self.item
219	}
220
221	/// Gets a reference to the wrapped value
222	///
223	/// Note: [`Deref`] is not implemented on purpose, to prevent
224	/// unintentional auto-derefs
225	// TODO: should we reconsider the above?
226	#[inline]
227	pub fn as_value_ref(&self) -> &T {
228		&self.item
229	}
230
231	/// Gets a mut reference to the wrapped value
232	///
233	/// Note: [`DerefMut`] is not implemented on purpose, to prevent
234	/// unintentional auto-derefs
235	// TODO: should we reconsider the above?
236	#[inline]
237	pub fn as_value_mut(&mut self) -> &mut T {
238		&mut self.item
239	}
240
241	/// Unwraps and rewraps the value as another nominal type, without modifying
242	/// the wrapped value
243	///
244	/// If you're using this function, make sure you know why you're using it!
245	/// after all, the whole point of this is to seperate otherwise identical
246	/// types into newtypes based on semantic meaning.
247	#[inline]
248	pub fn with_other_marker<M2>(self) -> Nominal<T, M2> {
249		Nominal::new(self.into_inner())
250	}
251
252	/// Consumes and maps the wrapped value into another value, wrapping it in
253	/// a nominal type with the same marker
254	#[inline]
255	pub fn map<T2, F>(self, f: F) -> Nominal<T2, M>
256	where
257		F: FnOnce(T) -> T2
258	{
259		Nominal::new(f(self.into_inner()))
260	}
261
262	/// Consumes and asyncronously maps the wrapped value into another value,
263	/// wrapping it in a nominal type with the same marker
264	#[inline]
265	pub async fn map_async<T2, F, Fu>(self, f: F) -> Nominal<T2, M>
266	where
267		F: FnOnce(T) -> Fu,
268		Fu: IntoFuture<Output = T2>
269	{
270		Nominal::new(f(self.into_inner()).await)
271	}
272}
273
274impl<T, M, E> Nominal<Result<T, E>, M> {
275	/// Transpose a nominal wrapped [`Result`] into a [`Result`] of a nominal
276	/// wrapped value
277	///
278	/// The value gets wrapped, but the error does not. Both are not otherwise
279	/// modified in any way.
280	#[inline]
281	pub fn transpose(self) -> Result<Nominal<T, M>, E> {
282		self.into_inner().map(Nominal::new)
283	}
284
285	/// Maps the [`Ok`] value of a [`Result`], wrapping the resulting [`Result`]
286	/// in a nominal type with the same marker
287	#[inline]
288	pub fn map_result_ok<T2, F>(self, f: F) -> Nominal<Result<T2, E>, M>
289	where
290		F: FnOnce(T) -> T2
291	{
292		Nominal::new(self.into_inner().map(f))
293	}
294
295	/// Maps the [`Err`] value of a [`Result`], wrapping the resulting [`Result`]
296	/// in a nominal type with the same marker
297	#[inline]
298	pub fn map_result_err<E2, F>(self, f: F) -> Nominal<Result<T, E2>, M>
299	where
300		F: FnOnce(E) -> E2
301	{
302		Nominal::new(self.into_inner().map_err(f))
303	}
304}
305
306impl<T, M> Nominal<Option<T>, M> {
307	/// Transpose a nominal wrapped [`Option`] into an [`Option`] of a nominal
308	/// wrapped value
309	///
310	/// The value is not otherwise modified in any way.
311	#[inline]
312	pub fn transpose(self) -> Option<Nominal<T, M>> {
313		self.into_inner().map(Nominal::new)
314	}
315
316	/// Maps the [`Some`] value of an [`Option`], wrapping the resulting [`Option`]
317	/// in a nominal type with the same marker
318	#[inline]
319	pub fn map_option_some<T2, F>(self, f: F) -> Nominal<Option<T2>, M>
320	where
321		F: FnOnce(T) -> T2
322	{
323		Nominal::new(self.into_inner().map(f))
324	}
325}
326
327impl<T, M> From<T> for Nominal<T, M> {
328	#[inline]
329	fn from(value: T) -> Self {
330		Self::new(value)
331	}
332}
333
334// delegate trait impls by just calling T's impl
335
336impl<T, M> Clone for Nominal<T, M>
337where
338	T: Clone
339{
340	#[inline]
341	fn clone(&self) -> Self {
342		Self::new(self.as_value_ref().clone())
343	}
344
345	#[inline]
346	fn clone_from(&mut self, source: &Self) {
347		self.as_value_mut().clone_from(source.as_value_ref())
348	}
349}
350
351impl<T, M> Copy for Nominal<T, M>
352where
353	T: Copy
354{}
355
356impl<T, M> Debug for Nominal<T, M>
357where
358	T: Debug
359{
360	#[inline]
361	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362		f.debug_struct("Nominal")
363			.field("value", self.as_value_ref())
364			.finish()
365	}
366}
367
368impl<T, M> Display for Nominal<T, M>
369where
370	T: Display
371{
372	#[inline]
373	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374		self.as_value_ref().fmt(f)
375	}
376}
377
378impl<T, M> Default for Nominal<T, M>
379where
380	T: Default
381{
382	#[inline]
383	fn default() -> Self {
384		Self::new(T::default())
385	}
386}
387
388impl<T, M> Hash for Nominal<T, M>
389where
390	T: Hash
391{
392	#[inline]
393	fn hash<H>(&self, state: &mut H)
394	where
395		H: Hasher
396	{
397		self.as_value_ref().hash(state)
398	}
399
400	#[inline]
401	fn hash_slice<H>(data: &[Self], state: &mut H)
402	where
403		Self: Sized,
404		H: Hasher
405	{
406		#[expect(clippy::as_conversions, reason = "ptr cast")]
407		let slice_ptr = data as *const [Self] as *const [T];
408
409		// SAFETY:
410		// - we're repr(transparent)
411		// - reborrowing `data` as slice of `T`
412		let slice = unsafe { &*slice_ptr };
413
414		T::hash_slice(slice, state)
415	}
416}
417
418impl<T, M, TR, MR> PartialEq<Nominal<TR, MR>> for Nominal<T, M>
419where
420	T: PartialEq<TR>
421{
422	#[inline]
423	fn eq(&self, other: &Nominal<TR, MR>) -> bool {
424		self.as_value_ref().eq(other.as_value_ref())
425	}
426
427	#[expect(
428		clippy::partialeq_ne_impl,
429		reason = "T might have overridden `ne`. we should use it if so"
430	)]
431	#[inline]
432	fn ne(&self, other: &Nominal<TR, MR>) -> bool {
433		self.as_value_ref().ne(other.as_value_ref())
434	}
435}
436
437impl<T, M> Eq for Nominal<T, M>
438where
439	T: Eq
440{}
441
442impl<T, M, TR, MR> PartialOrd<Nominal<TR, MR>> for Nominal<T, M>
443where
444	T: PartialOrd<TR>
445{
446	#[inline]
447	fn partial_cmp(&self, other: &Nominal<TR, MR>) -> Option<cmp::Ordering> {
448		self.as_value_ref().partial_cmp(other.as_value_ref())
449	}
450
451	#[inline]
452	fn lt(&self, other: &Nominal<TR, MR>) -> bool {
453		self.as_value_ref().lt(other.as_value_ref())
454	}
455
456	#[inline]
457	fn le(&self, other: &Nominal<TR, MR>) -> bool {
458		self.as_value_ref().le(other.as_value_ref())
459	}
460
461	#[inline]
462	fn gt(&self, other: &Nominal<TR, MR>) -> bool {
463		self.as_value_ref().gt(other.as_value_ref())
464	}
465
466	#[inline]
467	fn ge(&self, other: &Nominal<TR, MR>) -> bool {
468		self.as_value_ref().ge(other.as_value_ref())
469	}
470}
471
472impl<T, M> Ord for Nominal<T, M>
473where
474	T: Ord
475{
476	#[inline]
477	fn cmp(&self, other: &Self) -> cmp::Ordering {
478		self.as_value_ref().cmp(other.as_value_ref())
479	}
480
481	#[inline]
482	fn max(self, other: Self) -> Self {
483		Self::new(self.into_inner().max(other.into_inner()))
484	}
485
486	#[inline]
487	fn min(self, other: Self) -> Self {
488		Self::new(self.into_inner().min(other.into_inner()))
489	}
490
491	#[inline]
492	fn clamp(self, min: Self, max: Self) -> Self {
493		Self::new(self.into_inner().clamp(min.into_inner(), max.into_inner()))
494	}
495}