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	///
41	/// ## Examples
42	///
43	/// ```rust
44	/// # #[cfg(feature = "std")] {
45	/// use wyz::fmt::*;
46	///
47	/// assert_eq!(
48	///   format!("{:?}", 3.fmt_binary()),
49	///   "11",
50	/// );
51	/// # }
52	/// ```
53	#[inline(always)]
54	fn fmt_binary(self) -> FmtBinary<Self>
55	where Self: Binary {
56		FmtBinary(self)
57	}
58
59	/// Causes `self` to use its `Display` implementation when
60	/// `Debug`-formatted.
61	///
62	/// ## Examples
63	///
64	/// ```rust
65	/// # #[cfg(feature = "std")] {
66	/// use wyz::fmt::*;
67	///
68	/// let text = "hello,
69	/// world!";
70	/// assert_eq!(
71	///   format!("{:?}", text),
72	///   r#""hello,\nworld!""#,
73	///   // the render contains the sequence U+005C REVERSE SOLIDUS,
74	///   // then U+006E LATIN SMALL LETTER N, *not* U+000A LINE FEED.
75	/// );
76	/// assert_eq!(
77	///   format!("{:?}", text.fmt_display()),
78	///   "hello,\nworld!",
79	///   // the render does not have wrapping quotes, and
80	///   // the newline is printed directly, not escaped.
81	/// );
82	/// # }
83	/// ```
84	#[inline(always)]
85	fn fmt_display(self) -> FmtDisplay<Self>
86	where Self: Display {
87		FmtDisplay(self)
88	}
89
90	/// Causes `self` to use its `LowerExp` implementation when
91	/// `Debug`-formatted.
92	///
93	/// ## Examples
94	///
95	/// ```rust
96	/// # #[cfg(feature = "std")] {
97	/// use wyz::fmt::*;
98	///
99	/// assert_eq!(
100	///   format!("{:?}", (31.4).fmt_lower_exp()),
101	///   "3.14e1",
102	/// );
103	/// # }
104	/// ```
105	#[inline(always)]
106	fn fmt_lower_exp(self) -> FmtLowerExp<Self>
107	where Self: LowerExp {
108		FmtLowerExp(self)
109	}
110
111	/// Causes `self` to use its `LowerHex` implementation when
112	/// `Debug`-formatted.
113	///
114	/// ## Examples
115	///
116	/// ```rust
117	/// # #[cfg(feature = "std")] {
118	/// use wyz::fmt::*;
119	///
120	/// assert_eq!(
121	///   format!("{:?}", 44203.fmt_lower_hex()),
122	///   "acab",
123	/// );
124	/// # }
125	/// ```
126	#[inline(always)]
127	fn fmt_lower_hex(self) -> FmtLowerHex<Self>
128	where Self: LowerHex {
129		FmtLowerHex(self)
130	}
131
132	/// Causes `self` to use its `Octal` implementation when `Debug`-formatted.
133	///
134	/// ## Examples
135	///
136	/// ```rust
137	/// # #[cfg(feature = "std")] {
138	/// use wyz::fmt::*;
139	///
140	/// assert_eq!(
141	///   format!("{:?}", 493.fmt_octal()),
142	///   "755",
143	/// );
144	/// # }
145	/// ```
146	#[inline(always)]
147	fn fmt_octal(self) -> FmtOctal<Self>
148	where Self: Octal {
149		FmtOctal(self)
150	}
151
152	/// Causes `self` to use its `Pointer` implementation when
153	/// `Debug`-formatted.
154	///
155	/// ## Examples
156	///
157	/// ```rust
158	/// # #[cfg(feature = "std")] {
159	/// use wyz::fmt::*;
160	///
161	/// let val = 6;
162	/// let addr = &val as *const i32;
163	/// println!("{:?}", addr.fmt_pointer());
164	/// // prints a memory address.
165	/// # }
166	/// ```
167	#[inline(always)]
168	fn fmt_pointer(self) -> FmtPointer<Self>
169	where Self: Pointer {
170		FmtPointer(self)
171	}
172
173	/// Causes `self` to use its `UpperExp` implementation when
174	/// `Debug`-formatted.
175	///
176	/// ## Examples
177	///
178	/// ```rust
179	/// # #[cfg(feature = "std")] {
180	/// use wyz::fmt::*;
181	///
182	/// assert_eq!(
183	///   format!("{:?}", (62.8).fmt_upper_exp()),
184	///   "6.28E1",
185	/// );
186	/// # }
187	/// ```
188	#[inline(always)]
189	fn fmt_upper_exp(self) -> FmtUpperExp<Self>
190	where Self: UpperExp {
191		FmtUpperExp(self)
192	}
193
194	/// Causes `self` to use its `UpperHex` implementation when
195	/// `Debug`-formatted.
196	///
197	/// ## Examples
198	///
199	/// ```rust
200	/// # #[cfg(feature = "std")] {
201	/// use wyz::fmt::*;
202	///
203	/// assert_eq!(
204	///   format!("{:?}", 55322.fmt_upper_hex()),
205	///   "D81A",
206	/// );
207	/// # }
208	/// ```
209	#[inline(always)]
210	fn fmt_upper_hex(self) -> FmtUpperHex<Self>
211	where Self: UpperHex {
212		FmtUpperHex(self)
213	}
214
215	/// Formats each item in a sequence.
216	///
217	/// This wrapper structure conditionally implements all of the formatting
218	/// traits when `self` can be viewed as an iterator whose *items* implement
219	/// them. It iterates over `&self` and prints each item according to the
220	/// formatting specifier provided.
221	///
222	/// ## Examples
223	///
224	/// ```rust
225	/// # #[cfg(feature = "std")] {
226	/// use wyz::fmt::*;
227	///
228	/// let seq = [10, 20, 30, 40];
229	/// assert_eq!(
230	///   format!("{:?}", seq.fmt_list().fmt_lower_hex()),
231	///   "[a, 14, 1e, 28]",
232	/// );
233	/// # }
234	/// ```
235	#[inline(always)]
236	fn fmt_list(self) -> FmtList<Self>
237	where for<'a> &'a Self: IntoIterator {
238		FmtList(self)
239	}
240}
241
242impl<T: Sized> FmtForward for T {
243}
244
245/// Forwards a type’s `Binary` formatting implementation to `Debug`.
246#[repr(transparent)]
247pub struct FmtBinary<T: Binary>(pub T);
248
249/// Forwards a type’s `Display` formatting implementation to `Debug`.
250#[repr(transparent)]
251pub struct FmtDisplay<T: Display>(pub T);
252
253/// Renders each element of a stream into a list.
254#[repr(transparent)]
255pub struct FmtList<T>(pub T)
256where for<'a> &'a T: IntoIterator;
257
258/// Forwards a type’s `LowerExp` formatting implementation to `Debug`.
259#[repr(transparent)]
260pub struct FmtLowerExp<T: LowerExp>(pub T);
261
262/// Forwards a type’s `LowerHex` formatting implementation to `Debug`.
263#[repr(transparent)]
264pub struct FmtLowerHex<T: LowerHex>(pub T);
265
266/// Forwards a type’s `Octal` formatting implementation to `Debug`.
267#[repr(transparent)]
268pub struct FmtOctal<T: Octal>(pub T);
269
270/// Forwards a type’s `Pointer` formatting implementation to `Debug`.
271#[repr(transparent)]
272pub struct FmtPointer<T: Pointer>(pub T);
273
274/// Forwards a type’s `UpperExp` formatting implementation to `Debug`.
275#[repr(transparent)]
276pub struct FmtUpperExp<T: UpperExp>(pub T);
277
278/// Forwards a type’s `UpperHex` formatting implementation to `Debug`.
279#[repr(transparent)]
280pub struct FmtUpperHex<T: UpperHex>(pub T);
281
282macro_rules! fmt {
283	($($w:ty => $t:ident),* $(,)?) => { $(
284		#[cfg(not(tarpaulin_include))]
285		impl<T: $t + Binary> Binary for $w {
286			#[inline(always)]
287			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
288				Binary::fmt(&self.0, fmt)
289			}
290		}
291
292		impl<T: $t> Debug for $w {
293			#[inline(always)]
294			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
295				<T as $t>::fmt(&self.0, fmt)
296			}
297		}
298
299		#[cfg(not(tarpaulin_include))]
300		impl<T: $t + Display> Display for $w {
301			#[inline(always)]
302			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
303				Display::fmt(&self.0, fmt)
304			}
305		}
306
307		#[cfg(not(tarpaulin_include))]
308		impl<T: $t + LowerExp> LowerExp for $w {
309			#[inline(always)]
310			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
311				LowerExp::fmt(&self.0, fmt)
312			}
313		}
314
315		#[cfg(not(tarpaulin_include))]
316		impl<T: $t + LowerHex> LowerHex for $w {
317			#[inline(always)]
318			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
319				LowerHex::fmt(&self.0, fmt)
320			}
321		}
322
323		#[cfg(not(tarpaulin_include))]
324		impl<T: $t + Octal> Octal for $w {
325			#[inline(always)]
326			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
327				Octal::fmt(&self.0, fmt)
328			}
329		}
330
331		#[cfg(not(tarpaulin_include))]
332		impl<T: $t + Pointer> Pointer for $w {
333			#[inline(always)]
334			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
335				Pointer::fmt(&self.0, fmt)
336			}
337		}
338
339		#[cfg(not(tarpaulin_include))]
340		impl<T: $t + UpperExp> UpperExp for $w {
341			#[inline(always)]
342			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
343				UpperExp::fmt(&self.0, fmt)
344			}
345		}
346
347		#[cfg(not(tarpaulin_include))]
348		impl<T: $t + UpperHex> UpperHex for $w {
349			#[inline(always)]
350			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
351				UpperHex::fmt(&self.0, fmt)
352			}
353		}
354
355		#[cfg(not(tarpaulin_include))]
356		impl<T: $t> Deref for $w {
357			type Target = T;
358
359			#[inline(always)]
360			fn deref(&self) -> &Self::Target {
361				&self.0
362			}
363		}
364
365		#[cfg(not(tarpaulin_include))]
366		impl<T: $t> DerefMut for $w {
367			#[inline(always)]
368			fn deref_mut(&mut self) -> &mut Self::Target {
369				&mut self.0
370			}
371		}
372
373		#[cfg(not(tarpaulin_include))]
374		impl<T: $t> AsRef<T> for $w {
375			#[inline(always)]
376			fn as_ref(&self) -> &T {
377				&self.0
378			}
379		}
380
381		#[cfg(not(tarpaulin_include))]
382		impl<T: $t> AsMut<T> for $w {
383			#[inline(always)]
384			fn as_mut(&mut self) -> &mut T {
385				&mut self.0
386			}
387		}
388	)* };
389}
390
391fmt!(
392	FmtBinary<T> => Binary,
393	FmtDisplay<T> => Display,
394	FmtLowerExp<T> => LowerExp,
395	FmtLowerHex<T> => LowerHex,
396	FmtOctal<T> => Octal,
397	FmtPointer<T> => Pointer,
398	FmtUpperExp<T> => UpperExp,
399	FmtUpperHex<T> => UpperHex,
400);
401
402impl<T> Binary for FmtList<T>
403where
404	for<'a> &'a T: IntoIterator,
405	for<'a> <&'a T as IntoIterator>::Item: Binary,
406{
407	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
408		fmt.debug_list()
409			.entries((&self.0).into_iter().map(FmtBinary))
410			.finish()
411	}
412}
413
414impl<T> Debug for FmtList<T>
415where
416	for<'a> &'a T: IntoIterator,
417	for<'a> <&'a T as IntoIterator>::Item: Debug,
418{
419	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
420		fmt.debug_list().entries((&self.0).into_iter()).finish()
421	}
422}
423
424impl<T> Display for FmtList<T>
425where
426	for<'a> &'a T: IntoIterator,
427	for<'a> <&'a T as IntoIterator>::Item: Display,
428{
429	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
430		fmt.debug_list()
431			.entries((&self.0).into_iter().map(FmtDisplay))
432			.finish()
433	}
434}
435
436impl<T> LowerExp for FmtList<T>
437where
438	for<'a> &'a T: IntoIterator,
439	for<'a> <&'a T as IntoIterator>::Item: LowerExp,
440{
441	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
442		fmt.debug_list()
443			.entries((&self.0).into_iter().map(FmtLowerExp))
444			.finish()
445	}
446}
447
448impl<T> LowerHex for FmtList<T>
449where
450	for<'a> &'a T: IntoIterator,
451	for<'a> <&'a T as IntoIterator>::Item: LowerHex,
452{
453	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
454		fmt.debug_list()
455			.entries((&self.0).into_iter().map(FmtLowerHex))
456			.finish()
457	}
458}
459
460impl<T> Octal for FmtList<T>
461where
462	for<'a> &'a T: IntoIterator,
463	for<'a> <&'a T as IntoIterator>::Item: Octal,
464{
465	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
466		fmt.debug_list()
467			.entries((&self.0).into_iter().map(FmtOctal))
468			.finish()
469	}
470}
471
472impl<T> UpperExp for FmtList<T>
473where
474	for<'a> &'a T: IntoIterator,
475	for<'a> <&'a T as IntoIterator>::Item: UpperExp,
476{
477	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
478		fmt.debug_list()
479			.entries((&self.0).into_iter().map(FmtUpperExp))
480			.finish()
481	}
482}
483
484impl<T> UpperHex for FmtList<T>
485where
486	for<'a> &'a T: IntoIterator,
487	for<'a> <&'a T as IntoIterator>::Item: UpperHex,
488{
489	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
490		fmt.debug_list()
491			.entries((&self.0).into_iter().map(FmtUpperHex))
492			.finish()
493	}
494}
495
496#[cfg(not(tarpaulin_include))]
497impl<T> Deref for FmtList<T>
498where for<'a> &'a T: IntoIterator
499{
500	type Target = T;
501
502	#[inline(always)]
503	fn deref(&self) -> &Self::Target {
504		&self.0
505	}
506}
507
508#[cfg(not(tarpaulin_include))]
509impl<T> DerefMut for FmtList<T>
510where for<'a> &'a T: IntoIterator
511{
512	#[inline(always)]
513	fn deref_mut(&mut self) -> &mut Self::Target {
514		&mut self.0
515	}
516}
517
518#[cfg(not(tarpaulin_include))]
519impl<T> AsRef<T> for FmtList<T>
520where for<'a> &'a T: IntoIterator
521{
522	#[inline(always)]
523	fn as_ref(&self) -> &T {
524		&self.0
525	}
526}
527
528#[cfg(not(tarpaulin_include))]
529impl<T> AsMut<T> for FmtList<T>
530where for<'a> &'a T: IntoIterator
531{
532	#[inline(always)]
533	fn as_mut(&mut self) -> &mut T {
534		&mut self.0
535	}
536}
537
538#[cfg(all(test, feature = "alloc"))]
539mod tests {
540	#[cfg(not(feature = "std"))]
541	use alloc::format;
542
543	#[cfg(feature = "std")]
544	use std::format;
545
546	use super::*;
547
548	#[test]
549	fn render_item() {
550		let num = 29;
551
552		assert_eq!(format!("{:?}", num.fmt_binary()), "11101");
553		assert_eq!(format!("{:?}", num.fmt_display()), "29");
554		assert_eq!(format!("{:?}", num.fmt_upper_hex()), "1D");
555		assert_eq!(format!("{:?}", num.fmt_octal()), "35");
556		assert_eq!(format!("{:?}", num.fmt_lower_hex()), "1d");
557
558		let num = 53.7;
559		assert_eq!(format!("{:?}", num.fmt_lower_exp()), "5.37e1");
560		assert_eq!(format!("{:?}", num.fmt_upper_exp()), "5.37E1");
561	}
562
563	#[test]
564	fn render_list() {
565		let list = [0, 1, 2, 3];
566		assert_eq!(format!("{:02b}", list.fmt_list()), "[00, 01, 10, 11]");
567		assert_eq!(format!("{:01?}", list.fmt_list()), "[0, 1, 2, 3]");
568		assert_eq!(format!("{:01}", list.fmt_list()), "[0, 1, 2, 3]");
569
570		let list = [-51.0, -1.2, 1.3, 54.0];
571		assert_eq!(
572			format!("{:e}", list.fmt_list()),
573			"[-5.1e1, -1.2e0, 1.3e0, 5.4e1]"
574		);
575		assert_eq!(
576			format!("{:E}", list.fmt_list()),
577			"[-5.1E1, -1.2E0, 1.3E0, 5.4E1]"
578		);
579
580		let list = [0, 10, 20, 30];
581		assert_eq!(format!("{:02x}", list.fmt_list()), "[00, 0a, 14, 1e]");
582		assert_eq!(format!("{:02o}", list.fmt_list()), "[00, 12, 24, 36]");
583		assert_eq!(format!("{:02X}", list.fmt_list()), "[00, 0A, 14, 1E]");
584
585		assert_eq!(
586			format!("{:02?}", list.fmt_list().fmt_lower_hex()),
587			"[00, 0a, 14, 1e]"
588		);
589	}
590}