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}