iter_debug/lib.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2#![no_std]
3#![no_implicit_prelude]
4//! [](https://github.com/1e1001/rsutil/tree/main/iter-debug)
5//! [](https://crates.io/crates/iter-debug)
6//! [](https://docs.rs/iter-debug)
7//! [](https://github.com/1e1001/rsutil/blob/main/iter-debug/README.md#License)
8//!
9//! Allows debugging iterators without collecting them to a [`Vec`] first,
10//! useful in `no_std` environments or when you're lazy.
11//! ```rust
12//! # use iter_debug::DebugIterator;
13//! println!("{:?}", [1, 2, 3, 4].into_iter().map(|v| v * 2).debug());
14//! // => [2, 4, 6, 8]
15//! ```
16//!
17//! [`Vec`]: <https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html>
18extern crate core;
19
20use core::cell::Cell;
21use core::fmt::{Debug, Formatter, Result};
22use core::iter::IntoIterator;
23use core::marker::Sized;
24use core::option::Option;
25
26#[cfg(test)]
27mod tests;
28
29enum IterDebugStyle {
30 List,
31 Set,
32}
33
34/// The whole point, see the [crate docs](`crate`).
35///
36/// Note that an iterator can only be debugged once, aim to wrap your iterator
37/// as late as possible, usually directly in the print / format statement.
38pub struct IterDebug<T>(Cell<Option<T>>, IterDebugStyle);
39
40impl<T> IterDebug<T> {
41 /// Construct a new instance directly, instead of using the
42 /// [`debug`](DebugIterator::debug) method. Prints output as a list.
43 #[inline]
44 pub fn new(item: T) -> Self { Self(Cell::new(Option::Some(item)), IterDebugStyle::List) }
45 /// Construct a new instance directly, instead of using the
46 /// [`debug`](DebugIterator::debug) method. Prints output as a set.
47 #[inline]
48 pub fn new_set(item: T) -> Self { Self(Cell::new(Option::Some(item)), IterDebugStyle::Set) }
49 /// Attempt to extract the inner iterator, returning [`None`](Option::None)
50 /// if it has already been removed or debug printed.
51 #[inline]
52 pub fn try_into_inner(&self) -> Option<T> { self.0.take() }
53}
54
55impl<T> Debug for IterDebug<T>
56where
57 T: IntoIterator,
58 T::Item: Debug,
59{
60 #[inline]
61 fn fmt(&self, f: &mut Formatter) -> Result {
62 match (self.0.take(), &self.1) {
63 (Option::Some(value), IterDebugStyle::List) => f.debug_list().entries(value).finish(),
64 (Option::Some(value), IterDebugStyle::Set) => f.debug_set().entries(value).finish(),
65 (Option::None, _) => f.write_str("<consumed iterator>"),
66 }
67 }
68}
69
70/// [`IterDebug`] but styled with key-value formatting
71///
72/// Note that an iterator can only be debugged once, aim to wrap your iterator
73/// as late as possible, usually directly in the print / format statement.
74pub struct KvIterDebug<T>(Cell<Option<T>>);
75
76impl<T> KvIterDebug<T> {
77 /// Construct a new instance directly, instead of using the
78 /// [`debug`](DebugIterator::debug) method. Prints output as a map.
79 #[inline]
80 pub fn new(item: T) -> Self { Self(Cell::new(Option::Some(item))) }
81 /// Attempt to extract the inner iterator, returning [`None`](Option::None)
82 /// if it has already been removed or debug printed.
83 #[inline]
84 pub fn try_into_inner(&self) -> Option<T> { self.0.take() }
85}
86
87impl<T, K, V> Debug for KvIterDebug<T>
88where
89 T: IntoIterator<Item = (K, V)>,
90 K: Debug,
91 V: Debug,
92{
93 #[inline]
94 fn fmt(&self, f: &mut Formatter) -> Result {
95 match self.0.take() {
96 Option::Some(value) => f.debug_map().entries(value).finish(),
97 Option::None => f.write_str("<consumed iterator>"),
98 }
99 }
100}
101
102/// Helper trait that lets you `.debug()` an iterator, like the other
103/// combinators.
104///
105/// Automatically implemented for all [`IntoIterator`] with [`Debug`]-able
106/// items.
107pub trait DebugIterator {
108 /// Convert this iterator to a [`Debug`]-printable value, printed as a list.
109 fn debug(self) -> IterDebug<Self>
110 where
111 Self: Sized;
112 /// Convert this iterator to a [`Debug`]-printable value, printed as a set.
113 fn debug_set(self) -> IterDebug<Self>
114 where
115 Self: Sized;
116 /// Convert this iterator to a [`Debug`]-printable value, printed as a map.
117 fn debug_map(self) -> KvIterDebug<Self>
118 where
119 Self: Sized;
120}
121impl<T> DebugIterator for T
122where
123 T: IntoIterator,
124 // this isn't the exact bound for `KvIterDebug`,
125 // but it keeps the extension in one trait and is always true for (K, V) pairs
126 T::Item: Debug,
127{
128 #[inline]
129 fn debug(self) -> IterDebug<Self>
130 where
131 Self: Sized,
132 {
133 IterDebug::new(self)
134 }
135 #[inline]
136 fn debug_set(self) -> IterDebug<Self>
137 where
138 Self: Sized,
139 {
140 IterDebug::new_set(self)
141 }
142 fn debug_map(self) -> KvIterDebug<Self>
143 where
144 Self: Sized,
145 {
146 KvIterDebug::new(self)
147 }
148}