const_format/marker_traits/
format_marker.rs

1//! Marker trait for types that implement the const formatting methods.
2//!
3//!
4
5use crate::wrapper_types::PWrapper;
6
7use core::marker::PhantomData;
8
9////////////////////////////////////////////////////////////////////////////////
10
11/// Marker trait for types that implement the const formatting methods.
12///
13/// Debug formatting can be derived using the [`ConstDebug`] derive macro.
14///
15/// # Implementors
16///
17/// Types that implement this trait are also expected to implement at least one of
18/// these inherent methods:
19///
20/// ```ignore
21/// // use const_format::{Error, Format};
22///
23/// const fn const_debug_fmt(&self, &mut Formatter<'_>) -> Result<(), Error>
24///
25/// const fn const_display_fmt(&self, &mut Formatter<'_>) -> Result<(), Error>
26/// ```
27///
28/// # Coercions
29///
30/// The [`Kind`](#associatedtype.Kind) and [`This`](#associatedtype.This) associated types
31/// are used in the [`IsAFormatMarker`] marker type
32/// to automatically wrap types in [`PWrapper`] if they're from the standard library,
33/// otherwise leaving them unwrapped.
34///
35/// # Examples
36///
37/// ### Display formatting
38///
39/// This example demonstrates how you can implement display formatting,
40/// without using the [`impl_fmt`] macro.
41///
42/// ```rust
43///
44/// use const_format::{
45///     marker_traits::{FormatMarker, IsNotStdKind},
46///     Error, Formatter, StrWriter,
47///     formatc, writec,
48/// };
49///
50/// use std::cmp::Ordering;
51///
52///
53/// struct Compared(u32, Ordering, u32);
54///
55/// // This is what the `impl_fmt` macro implements implicitly for all non-std types
56/// impl FormatMarker for Compared {
57///     type Kind = IsNotStdKind;
58///     type This = Self;
59/// }
60///
61/// impl Compared {
62///     pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
63///         let op = match self.1 {
64///             Ordering::Less => "<",
65///             Ordering::Equal => "==",
66///             Ordering::Greater => ">",
67///         };
68///         writec!(f, "{} {} {}", self.0, op, self.2)
69///     }
70/// }
71///
72/// const S_0: &str = formatc!("{}", Compared(0, Ordering::Less, 1));
73/// const S_1: &str = formatc!("{}", Compared(1, Ordering::Equal, 1));
74/// const S_2: &str = formatc!("{}", Compared(2, Ordering::Greater, 1));
75///
76/// assert_eq!(S_0, "0 < 1");
77/// assert_eq!(S_1, "1 == 1");
78/// assert_eq!(S_2, "2 > 1");
79///
80/// ```
81///
82/// ### Debug formatting
83///
84/// For examples of using the [`ConstDebug`] derive macro,
85/// [look here](crate::ConstDebug#examples).
86///
87/// These are examples of implementing debug formatting using the `impl_fmt` macro for:
88///
89/// - [Tupled structs and tuple variants.](../fmt/struct.DebugTuple.html#example)
90///
91/// - [Braced structs and braced variants.](../fmt/struct.DebugStruct.html#example)
92///
93///
94/// [`IsAFormatMarker`]: ./struct.IsAFormatMarker.html
95/// [`ConstDebug`]: crate::ConstDebug
96/// [`impl_fmt`]: ../macro.impl_fmt.html
97///
98pub trait FormatMarker {
99    /// What kind of type this is, this can be one of:
100    ///
101    /// - [`IsArrayKind`]: For slices, and arrays.
102    ///
103    /// - [`IsStdKind`]: Any other standard library type.
104    ///
105    /// - [`IsNotStdKind`]: Any type that is not from the standard library.
106    ///
107    /// [`IsArrayKind`]: ./struct.IsArrayKind.html
108    /// [`IsStdKind`]: ./struct.IsStdKind.html
109    /// [`IsNotStdKind`]: ./struct.IsNotStdKind.html
110    type Kind;
111
112    /// The type after dereferencing,
113    /// implemented as `type This = Self;` for all non-reference types
114    type This: ?Sized;
115}
116
117/// Marker type for arrays and slices,
118/// used as the [`Kind`] associated type  in [`FormatMarker`].
119///
120/// [`Kind`]: ./trait.FormatMarker.html#associatedtype.Kind
121///
122pub struct IsArrayKind<T>(PhantomData<T>);
123
124/// Marker type for the remaining standard library types,,
125/// used as the [`Kind`] associated type  in [`FormatMarker`].
126///
127/// [`Kind`]: ./trait.FormatMarker.html#associatedtype.Kind
128///
129pub struct IsStdKind;
130
131/// Marker type for non-standard library types,
132/// used as the [`Kind`] associated type  in [`FormatMarker`].
133///
134/// [`Kind`]: ./trait.FormatMarker.html#associatedtype.Kind
135///
136pub struct IsNotStdKind;
137
138macro_rules! std_kind_impls {
139    ($($ty:ty),* $(,)* ) => (
140        $(
141            impl FormatMarker for $ty {
142                type Kind = IsStdKind;
143                type This = Self;
144            }
145
146            impl<T> IsAFormatMarker<IsStdKind, $ty, T> {
147                /// Copies the value from `reference`, and wraps it in a `PWrapper`
148                #[inline(always)]
149                pub const fn coerce(self, reference: &$ty) -> PWrapper<$ty> {
150                    PWrapper(*reference)
151                }
152            }
153        )*
154    )
155}
156
157///////////////////////////////////////////////////////////////////////////////
158
159/// Hack used to automatically wrap standard library types inside [`PWrapper`],
160/// while leaving user defined types unwrapped.
161///
162/// # Type parameters
163///
164/// `K` is `<R as FormatMarker>::Kind`
165/// The kind of type that `T` is,
166/// [a slice](./struct.IsArrayKind.html),
167/// [other std types](./struct.IsStdKind.html),
168/// [non-std types](./struct.IsNotStdKind.html).
169///
170/// `T` is `<R as FormatMarker>::This`:
171/// The `R` type after removing all layers of references.
172///
173/// `R`: a type that implements [`FormatMarker`].
174///
175/// # Coerce Method
176///
177/// The `coerce` method is what does the conversion from a `&T` depending on
178/// the `K` type parameter:
179///
180/// - [`IsArrayKind`]: the reference is coerced to a slice, and wrapped in a [`PWrapper`].
181///
182/// - [`IsStdKind`]: the referenced value is copied, and wrapped in a [`PWrapper`].
183///
184/// - [`IsNotStdKind`]: the reference is simply returned as a `&T`.
185///
186#[allow(clippy::type_complexity)]
187pub struct IsAFormatMarker<K, T: ?Sized, R: ?Sized>(
188    PhantomData<(
189        PhantomData<fn() -> PhantomData<K>>,
190        PhantomData<fn() -> PhantomData<T>>,
191        PhantomData<fn() -> PhantomData<R>>,
192    )>,
193);
194
195impl<K, T: ?Sized, R: ?Sized> Copy for IsAFormatMarker<K, T, R> {}
196
197impl<K, T: ?Sized, R: ?Sized> Clone for IsAFormatMarker<K, T, R> {
198    fn clone(&self) -> Self {
199        *self
200    }
201}
202
203impl<R> IsAFormatMarker<R::Kind, R::This, R>
204where
205    R: ?Sized + FormatMarker,
206{
207    /// Constructs an `IsAFormatMarker`
208    pub const NEW: Self = Self(PhantomData);
209}
210
211impl<K, T: ?Sized, R: ?Sized> IsAFormatMarker<K, T, R> {
212    /// Infers the type parameters by taking a reference to `R` .
213    ///
214    /// The `K` and `T` type parameters are determined by `R` in
215    /// the [`NEW`] associated constant.
216    ///
217    /// [`NEW`]: #associatedconstant.NEW
218    #[inline(always)]
219    pub const fn infer_type(self, _: &R) -> Self {
220        self
221    }
222
223    /// Removes layers of references by coercing the argument.
224    #[inline(always)]
225    pub const fn unreference(self, r: &T) -> &T {
226        r
227    }
228}
229
230/////////////////////////////////////////////////////////////////////////////
231
232impl<U, T: ?Sized, R: ?Sized> IsAFormatMarker<IsArrayKind<U>, T, R> {
233    /// Coerces an array to a slice, then wraps the slice in a `PWrapper`
234    #[inline(always)]
235    pub const fn coerce(self, slice: &[U]) -> PWrapper<&[U]> {
236        PWrapper(slice)
237    }
238}
239
240impl<T: ?Sized, R: ?Sized> IsAFormatMarker<IsNotStdKind, T, R> {
241    /// An identity function, just takes `reference` and returns it.
242    #[inline(always)]
243    pub const fn coerce(self, reference: &T) -> &T {
244        reference
245    }
246}
247
248/////////////////////////////////////////////////////////////////////////////
249
250std_kind_impls! {
251    i8, u8,
252    i16, u16,
253    i32, u32,
254    i64, u64,
255    i128, u128,
256    isize, usize,
257    bool, char,
258}
259
260impl FormatMarker for str {
261    type Kind = IsStdKind;
262    type This = Self;
263}
264
265impl<R: ?Sized> IsAFormatMarker<IsStdKind, str, R> {
266    /// Wraps `reference` in a `PWrapper`.
267    #[inline(always)]
268    pub const fn coerce(self, reference: &str) -> PWrapper<&str> {
269        PWrapper(reference)
270    }
271}
272
273impl<T, const N: usize> FormatMarker for [T; N] {
274    type Kind = IsArrayKind<T>;
275    type This = Self;
276}
277
278impl<T> FormatMarker for [T] {
279    type Kind = IsArrayKind<T>;
280    type This = [T];
281}
282
283impl<T> FormatMarker for &T
284where
285    T: ?Sized + FormatMarker,
286{
287    type Kind = T::Kind;
288    type This = T::This;
289}
290
291impl<T> FormatMarker for &mut T
292where
293    T: ?Sized + FormatMarker,
294{
295    type Kind = T::Kind;
296    type This = T::This;
297}