Skip to main content

iter_debug/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2#![no_std]
3#![no_implicit_prelude]
4//! [![Repository](https://img.shields.io/badge/repository-GitHub-brightgreen.svg)](https://github.com/1e1001/rsutil/tree/main/iter-debug)
5//! [![Crates.io](https://img.shields.io/crates/v/iter-debug)](https://crates.io/crates/iter-debug)
6//! [![docs.rs](https://img.shields.io/docsrs/iter-debug)](https://docs.rs/iter-debug)
7//! [![MIT OR Apache-2.0](https://img.shields.io/crates/l/iter-debug)](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}