wyz/
fmt.rs

1/*! Format forwarding
2
3This module provides wrapper types for each formatting trait other than `Debug`
4which, when `Debug`-formatted, forward to the original trait instead of `Debug`.
5
6Each wrapper type is a tuple struct so that it can be used as a named
7constructor, such as in `.map(FmtDisplay)`. In addition, a blanket trait adds
8extension methods `.fmt_<trait_name>>()` to provide the corresponding wrap.
9
10Any modifiers in the format template string or struct modifier are passed
11through to the desired trait implementation unchanged. The only effect of the
12forwarding types in this module is to change the `?` template character to one
13of the other trait signifiers.
14!*/
15
16use core::{
17	fmt::{
18		self,
19		Binary,
20		Debug,
21		Display,
22		Formatter,
23		LowerExp,
24		LowerHex,
25		Octal,
26		Pointer,
27		UpperExp,
28		UpperHex,
29	},
30	ops::{
31		Deref,
32		DerefMut,
33	},
34};
35
36/// Wraps any value with a format-forward to `Debug`.
37#[cfg(not(tarpaulin_include))]
38pub trait FmtForward: Sized {
39	/// Causes `self` to use its `Binary` implementation when `Debug`-formatted.
40	#[inline(always)]
41	fn fmt_binary(self) -> FmtBinary<Self>
42	where Self: Binary {
43		FmtBinary(self)
44	}
45
46	/// Causes `self` to use its `Display` implementation when
47	/// `Debug`-formatted.
48	#[inline(always)]
49	fn fmt_display(self) -> FmtDisplay<Self>
50	where Self: Display {
51		FmtDisplay(self)
52	}
53
54	/// Causes `self` to use its `LowerExp` implementation when
55	/// `Debug`-formatted.
56	#[inline(always)]
57	fn fmt_lower_exp(self) -> FmtLowerExp<Self>
58	where Self: LowerExp {
59		FmtLowerExp(self)
60	}
61
62	/// Causes `self` to use its `LowerHex` implementation when
63	/// `Debug`-formatted.
64	#[inline(always)]
65	fn fmt_lower_hex(self) -> FmtLowerHex<Self>
66	where Self: LowerHex {
67		FmtLowerHex(self)
68	}
69
70	/// Causes `self` to use its `Octal` implementation when `Debug`-formatted.
71	#[inline(always)]
72	fn fmt_octal(self) -> FmtOctal<Self>
73	where Self: Octal {
74		FmtOctal(self)
75	}
76
77	/// Causes `self` to use its `Pointer` implementation when
78	/// `Debug`-formatted.
79	#[inline(always)]
80	fn fmt_pointer(self) -> FmtPointer<Self>
81	where Self: Pointer {
82		FmtPointer(self)
83	}
84
85	/// Causes `self` to use its `UpperExp` implementation when
86	/// `Debug`-formatted.
87	#[inline(always)]
88	fn fmt_upper_exp(self) -> FmtUpperExp<Self>
89	where Self: UpperExp {
90		FmtUpperExp(self)
91	}
92
93	/// Causes `self` to use its `UpperHex` implementation when
94	/// `Debug`-formatted.
95	#[inline(always)]
96	fn fmt_upper_hex(self) -> FmtUpperHex<Self>
97	where Self: UpperHex {
98		FmtUpperHex(self)
99	}
100
101	/// Formats each item in a sequence.
102	///
103	/// This wrapper structure conditionally implements all of the formatting
104	/// traits when `self` can be viewed as an iterator whose *items* implement
105	/// them. It iterates over `&self` and prints each item according to the
106	/// formatting specifier provided.
107	#[inline(always)]
108	fn fmt_list(self) -> FmtList<Self>
109	where for<'a> &'a Self: IntoIterator {
110		FmtList(self)
111	}
112}
113
114impl<T: Sized> FmtForward for T {
115}
116
117/// Forwards a type’s `Binary` formatting implementation to `Debug`.
118#[repr(transparent)]
119pub struct FmtBinary<T: Binary>(pub T);
120
121/// Forwards a type’s `Display` formatting implementation to `Debug`.
122#[repr(transparent)]
123pub struct FmtDisplay<T: Display>(pub T);
124
125/// Renders each element of a stream into a list.
126#[repr(transparent)]
127pub struct FmtList<T>(pub T)
128where for<'a> &'a T: IntoIterator;
129
130/// Forwards a type’s `LowerExp` formatting implementation to `Debug`.
131#[repr(transparent)]
132pub struct FmtLowerExp<T: LowerExp>(pub T);
133
134/// Forwards a type’s `LowerHex` formatting implementation to `Debug`.
135#[repr(transparent)]
136pub struct FmtLowerHex<T: LowerHex>(pub T);
137
138/// Forwards a type’s `Octal` formatting implementation to `Debug`.
139#[repr(transparent)]
140pub struct FmtOctal<T: Octal>(pub T);
141
142/// Forwards a type’s `Pointer` formatting implementation to `Debug`.
143#[repr(transparent)]
144pub struct FmtPointer<T: Pointer>(pub T);
145
146/// Forwards a type’s `UpperExp` formatting implementation to `Debug`.
147#[repr(transparent)]
148pub struct FmtUpperExp<T: UpperExp>(pub T);
149
150/// Forwards a type’s `UpperHex` formatting implementation to `Debug`.
151#[repr(transparent)]
152pub struct FmtUpperHex<T: UpperHex>(pub T);
153
154macro_rules! fmt {
155	($($w:ty => $t:ident),* $(,)?) => { $(
156		#[cfg(not(tarpaulin_include))]
157		impl<T: $t + Binary> Binary for $w {
158			#[inline(always)]
159			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
160				Binary::fmt(&self.0, fmt)
161			}
162		}
163
164		impl<T: $t> Debug for $w {
165			#[inline(always)]
166			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
167				<T as $t>::fmt(&self.0, fmt)
168			}
169		}
170
171		#[cfg(not(tarpaulin_include))]
172		impl<T: $t + Display> Display for $w {
173			#[inline(always)]
174			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
175				Display::fmt(&self.0, fmt)
176			}
177		}
178
179		#[cfg(not(tarpaulin_include))]
180		impl<T: $t + LowerExp> LowerExp for $w {
181			#[inline(always)]
182			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
183				LowerExp::fmt(&self.0, fmt)
184			}
185		}
186
187		#[cfg(not(tarpaulin_include))]
188		impl<T: $t + LowerHex> LowerHex for $w {
189			#[inline(always)]
190			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
191				LowerHex::fmt(&self.0, fmt)
192			}
193		}
194
195		#[cfg(not(tarpaulin_include))]
196		impl<T: $t + Octal> Octal for $w {
197			#[inline(always)]
198			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
199				Octal::fmt(&self.0, fmt)
200			}
201		}
202
203		#[cfg(not(tarpaulin_include))]
204		impl<T: $t + Pointer> Pointer for $w {
205			#[inline(always)]
206			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
207				Pointer::fmt(&self.0, fmt)
208			}
209		}
210
211		#[cfg(not(tarpaulin_include))]
212		impl<T: $t + UpperExp> UpperExp for $w {
213			#[inline(always)]
214			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
215				UpperExp::fmt(&self.0, fmt)
216			}
217		}
218
219		#[cfg(not(tarpaulin_include))]
220		impl<T: $t + UpperHex> UpperHex for $w {
221			#[inline(always)]
222			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
223				UpperHex::fmt(&self.0, fmt)
224			}
225		}
226
227		#[cfg(not(tarpaulin_include))]
228		impl<T: $t> Deref for $w {
229			type Target = T;
230
231			#[inline(always)]
232			fn deref(&self) -> &Self::Target {
233				&self.0
234			}
235		}
236
237		#[cfg(not(tarpaulin_include))]
238		impl<T: $t> DerefMut for $w {
239			#[inline(always)]
240			fn deref_mut(&mut self) -> &mut Self::Target {
241				&mut self.0
242			}
243		}
244
245		#[cfg(not(tarpaulin_include))]
246		impl<T: $t> AsRef<T> for $w {
247			#[inline(always)]
248			fn as_ref(&self) -> &T {
249				&self.0
250			}
251		}
252
253		#[cfg(not(tarpaulin_include))]
254		impl<T: $t> AsMut<T> for $w {
255			#[inline(always)]
256			fn as_mut(&mut self) -> &mut T {
257				&mut self.0
258			}
259		}
260	)* };
261}
262
263fmt!(
264	FmtBinary<T> => Binary,
265	FmtDisplay<T> => Display,
266	FmtLowerExp<T> => LowerExp,
267	FmtLowerHex<T> => LowerHex,
268	FmtOctal<T> => Octal,
269	FmtPointer<T> => Pointer,
270	FmtUpperExp<T> => UpperExp,
271	FmtUpperHex<T> => UpperHex,
272);
273
274impl<T> Binary for FmtList<T>
275where
276	for<'a> &'a T: IntoIterator,
277	for<'a> <&'a T as IntoIterator>::Item: Binary,
278{
279	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
280		fmt.debug_list()
281			.entries((&self.0).into_iter().map(FmtBinary))
282			.finish()
283	}
284}
285
286impl<T> Debug for FmtList<T>
287where
288	for<'a> &'a T: IntoIterator,
289	for<'a> <&'a T as IntoIterator>::Item: Debug,
290{
291	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
292		fmt.debug_list().entries((&self.0).into_iter()).finish()
293	}
294}
295
296impl<T> Display for FmtList<T>
297where
298	for<'a> &'a T: IntoIterator,
299	for<'a> <&'a T as IntoIterator>::Item: Display,
300{
301	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
302		fmt.debug_list()
303			.entries((&self.0).into_iter().map(FmtDisplay))
304			.finish()
305	}
306}
307
308impl<T> LowerExp for FmtList<T>
309where
310	for<'a> &'a T: IntoIterator,
311	for<'a> <&'a T as IntoIterator>::Item: LowerExp,
312{
313	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
314		fmt.debug_list()
315			.entries((&self.0).into_iter().map(FmtLowerExp))
316			.finish()
317	}
318}
319
320impl<T> LowerHex for FmtList<T>
321where
322	for<'a> &'a T: IntoIterator,
323	for<'a> <&'a T as IntoIterator>::Item: LowerHex,
324{
325	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
326		fmt.debug_list()
327			.entries((&self.0).into_iter().map(FmtLowerHex))
328			.finish()
329	}
330}
331
332impl<T> Octal for FmtList<T>
333where
334	for<'a> &'a T: IntoIterator,
335	for<'a> <&'a T as IntoIterator>::Item: Octal,
336{
337	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
338		fmt.debug_list()
339			.entries((&self.0).into_iter().map(FmtOctal))
340			.finish()
341	}
342}
343
344impl<T> UpperExp for FmtList<T>
345where
346	for<'a> &'a T: IntoIterator,
347	for<'a> <&'a T as IntoIterator>::Item: UpperExp,
348{
349	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
350		fmt.debug_list()
351			.entries((&self.0).into_iter().map(FmtUpperExp))
352			.finish()
353	}
354}
355
356impl<T> UpperHex for FmtList<T>
357where
358	for<'a> &'a T: IntoIterator,
359	for<'a> <&'a T as IntoIterator>::Item: UpperHex,
360{
361	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
362		fmt.debug_list()
363			.entries((&self.0).into_iter().map(FmtUpperHex))
364			.finish()
365	}
366}
367
368#[cfg(not(tarpaulin_include))]
369impl<T> Deref for FmtList<T>
370where for<'a> &'a T: IntoIterator
371{
372	type Target = T;
373
374	#[inline(always)]
375	fn deref(&self) -> &Self::Target {
376		&self.0
377	}
378}
379
380#[cfg(not(tarpaulin_include))]
381impl<T> DerefMut for FmtList<T>
382where for<'a> &'a T: IntoIterator
383{
384	#[inline(always)]
385	fn deref_mut(&mut self) -> &mut Self::Target {
386		&mut self.0
387	}
388}
389
390#[cfg(not(tarpaulin_include))]
391impl<T> AsRef<T> for FmtList<T>
392where for<'a> &'a T: IntoIterator
393{
394	#[inline(always)]
395	fn as_ref(&self) -> &T {
396		&self.0
397	}
398}
399
400#[cfg(not(tarpaulin_include))]
401impl<T> AsMut<T> for FmtList<T>
402where for<'a> &'a T: IntoIterator
403{
404	#[inline(always)]
405	fn as_mut(&mut self) -> &mut T {
406		&mut self.0
407	}
408}
409
410#[cfg(all(test, feature = "alloc"))]
411mod tests {
412	#[cfg(not(feature = "std"))]
413	use alloc::format;
414
415	#[cfg(feature = "std")]
416	use std::format;
417
418	use super::*;
419
420	#[test]
421	fn render_item() {
422		let num = 29;
423
424		assert_eq!(format!("{:?}", num.fmt_binary()), "11101");
425		assert_eq!(format!("{:?}", num.fmt_display()), "29");
426		assert_eq!(format!("{:?}", num.fmt_upper_hex()), "1D");
427		assert_eq!(format!("{:?}", num.fmt_octal()), "35");
428		assert_eq!(format!("{:?}", num.fmt_lower_hex()), "1d");
429
430		let num = 53.7;
431		assert_eq!(format!("{:?}", num.fmt_lower_exp()), "5.37e1");
432		assert_eq!(format!("{:?}", num.fmt_upper_exp()), "5.37E1");
433	}
434
435	#[test]
436	fn render_list() {
437		let list = [0, 1, 2, 3];
438		assert_eq!(format!("{:02b}", list.fmt_list()), "[00, 01, 10, 11]");
439		assert_eq!(format!("{:01?}", list.fmt_list()), "[0, 1, 2, 3]");
440		assert_eq!(format!("{:01}", list.fmt_list()), "[0, 1, 2, 3]");
441
442		let list = [-51.0, -1.2, 1.3, 54.0];
443		assert_eq!(
444			format!("{:e}", list.fmt_list()),
445			"[-5.1e1, -1.2e0, 1.3e0, 5.4e1]"
446		);
447		assert_eq!(
448			format!("{:E}", list.fmt_list()),
449			"[-5.1E1, -1.2E0, 1.3E0, 5.4E1]"
450		);
451
452		let list = [0, 10, 20, 30];
453		assert_eq!(format!("{:02x}", list.fmt_list()), "[00, 0a, 14, 1e]");
454		assert_eq!(format!("{:02o}", list.fmt_list()), "[00, 12, 24, 36]");
455		assert_eq!(format!("{:02X}", list.fmt_list()), "[00, 0A, 14, 1E]");
456	}
457}