valuable/listable.rs
1use crate::*;
2
3use core::fmt;
4
5/// A list-like [`Valuable`] sub-type.
6///
7/// Implemented by [`Valuable`] types that have a list-like shape. This includes
8/// [`Vec`] and other Rust [collection] types. `Listable` types may or may not
9/// store items in contiguous memory. Any type that implements [`IntoIterator`]
10/// may implement `Listable`. Values that implement `Listable` must return
11/// [`Value::Listable`] from their [`Valuable::as_value`] implementation.
12///
13/// [collection]: https://doc.rust-lang.org/stable/std/collections/index.html
14///
15/// # Inspecting
16///
17/// Inspecting `Listable` items is done by visiting the collection. When
18/// visiting a `Listable`, contained values are either passed one-by-one by
19/// repeatedly calling [`visit_value()`] or all at once by calling
20/// [`visit_primitive_slice()`]. The [`visit_primitive_slice()`] method has
21/// lower overhead but can only be used when the `Listable` type contains
22/// primitive values.
23///
24/// See [`Visit`] documentation for more details.
25///
26/// # Implementing
27///
28/// If the type stores values in slices internally, then those slices are passed
29/// to [`Valuable::visit_slice`], which handles calling
30/// [`visit_primitive_slice()`] if possible.
31///
32/// [`visit_value()`]: Visit::visit_value
33/// [`visit_primitive_slice()`]: Visit::visit_primitive_slice
34///
35/// ```
36/// use valuable::{Listable, Valuable, Value, Visit};
37///
38/// struct MyCollection<T> {
39/// chunks: Vec<Vec<T>>,
40/// }
41///
42/// impl<T: Valuable> Valuable for MyCollection<T> {
43/// fn as_value(&self) -> Value<'_> {
44/// Value::Listable(self)
45/// }
46///
47/// fn visit(&self, visit: &mut dyn Visit) {
48/// for chunk in &self.chunks {
49/// // Handles visiting the slice
50/// Valuable::visit_slice(chunk, visit);
51/// }
52/// }
53/// }
54///
55/// impl<T: Valuable> Listable for MyCollection<T> {
56/// fn size_hint(&self) -> (usize, Option<usize>) {
57/// let len = self.chunks.iter().map(|chunk| chunk.len()).sum();
58/// (len, Some(len))
59/// }
60/// }
61/// ```
62pub trait Listable: Valuable {
63 /// Returns the bounds on the remaining length of the `Listable`.
64 ///
65 /// Specifically, `size_hint()` returns a tuple where the first element
66 /// is the lower bound, and the second element is the upper bound.
67 ///
68 /// The second half of the tuple that is returned is an [`Option`]`<`[`usize`]`>`.
69 /// A [`None`] here means that either there is no known upper bound, or the
70 /// upper bound is larger than [`usize`].
71 ///
72 /// # Implementation notes
73 ///
74 /// It is not enforced that a `Listable` implementation yields the declared
75 /// number of elements. A buggy iterator may yield less than the lower bound
76 /// or more than the upper bound of elements.
77 ///
78 /// `size_hint()` is primarily intended to be used for optimizations such as
79 /// reserving space for the elements of the `Listable`, but must not be
80 /// trusted to e.g., omit bounds checks in unsafe code. An incorrect
81 /// implementation of `size_hint()` should not lead to memory safety
82 /// violations.
83 ///
84 /// That said, the implementation should provide a correct estimation,
85 /// because otherwise it would be a violation of the trait's protocol.
86 ///
87 /// [`usize`]: type@usize
88 ///
89 /// # Examples
90 ///
91 /// Basic usage:
92 ///
93 /// ```
94 /// use valuable::Listable;
95 ///
96 /// let a = vec![1, 2, 3];
97 ///
98 /// assert_eq!((3, Some(3)), a.size_hint());
99 /// ```
100 fn size_hint(&self) -> (usize, Option<usize>);
101}
102
103macro_rules! deref {
104 (
105 $(
106 $(#[$attrs:meta])*
107 $ty:ty,
108 )*
109 ) => {
110 $(
111 $(#[$attrs])*
112 impl<T: ?Sized + Listable> Listable for $ty {
113 fn size_hint(&self) -> (usize, Option<usize>) {
114 T::size_hint(&**self)
115 }
116 }
117 )*
118 };
119}
120
121deref! {
122 &T,
123 &mut T,
124 #[cfg(feature = "alloc")]
125 alloc::boxed::Box<T>,
126 #[cfg(feature = "alloc")]
127 alloc::rc::Rc<T>,
128 #[cfg(not(valuable_no_atomic_cas))]
129 #[cfg(feature = "alloc")]
130 alloc::sync::Arc<T>,
131}
132
133macro_rules! slice {
134 (
135 $(
136 $(#[$attrs:meta])*
137 ($($generics:tt)*) $ty:ty,
138 )*
139 ) => {
140 $(
141 $(#[$attrs])*
142 impl<$($generics)*> Valuable for $ty {
143 fn as_value(&self) -> Value<'_> {
144 Value::Listable(self as &dyn Listable)
145 }
146
147 fn visit(&self, visit: &mut dyn Visit) {
148 T::visit_slice(self, visit);
149 }
150 }
151
152 $(#[$attrs])*
153 impl<$($generics)*> Listable for $ty {
154 fn size_hint(&self) -> (usize, Option<usize>) {
155 (self.len(), Some(self.len()))
156 }
157 }
158 )*
159 };
160}
161
162slice! {
163 (T: Valuable) &'_ [T],
164 #[cfg(feature = "alloc")]
165 (T: Valuable) alloc::boxed::Box<[T]>,
166 #[cfg(feature = "alloc")]
167 (T: Valuable) alloc::rc::Rc<[T]>,
168 #[cfg(not(valuable_no_atomic_cas))]
169 #[cfg(feature = "alloc")]
170 (T: Valuable) alloc::sync::Arc<[T]>,
171 (T: Valuable, const N: usize) [T; N],
172 #[cfg(feature = "alloc")]
173 (T: Valuable) alloc::vec::Vec<T>,
174}
175
176macro_rules! collection {
177 (
178 $(
179 $(#[$attrs:meta])*
180 ($($generics:tt)*) $ty:ty,
181 )*
182 ) => {
183 $(
184 $(#[$attrs])*
185 impl<$($generics)*> Valuable for $ty {
186 fn as_value(&self) -> Value<'_> {
187 Value::Listable(self as &dyn Listable)
188 }
189
190 fn visit(&self, visit: &mut dyn Visit) {
191 for value in self.iter() {
192 visit.visit_value(value.as_value());
193 }
194 }
195 }
196
197 $(#[$attrs])*
198 impl<$($generics)*> Listable for $ty {
199 fn size_hint(&self) -> (usize, Option<usize>) {
200 (self.len(), Some(self.len()))
201 }
202 }
203 )*
204 };
205}
206
207collection! {
208 #[cfg(feature = "alloc")]
209 (T: Valuable) alloc::collections::LinkedList<T>,
210 #[cfg(feature = "alloc")]
211 (T: Valuable + Ord) alloc::collections::BinaryHeap<T>,
212 #[cfg(feature = "alloc")]
213 (T: Valuable + Ord) alloc::collections::BTreeSet<T>,
214 #[cfg(feature = "std")]
215 (T: Valuable + Eq + std::hash::Hash, H: std::hash::BuildHasher) std::collections::HashSet<T, H>,
216}
217
218#[cfg(feature = "alloc")]
219impl<T: Valuable> Valuable for alloc::collections::VecDeque<T> {
220 fn as_value(&self) -> Value<'_> {
221 Value::Listable(self as &dyn Listable)
222 }
223
224 fn visit(&self, visit: &mut dyn Visit) {
225 let (first, second) = self.as_slices();
226 T::visit_slice(first, visit);
227 T::visit_slice(second, visit);
228 }
229}
230
231#[cfg(feature = "alloc")]
232impl<T: Valuable> Listable for alloc::collections::VecDeque<T> {
233 fn size_hint(&self) -> (usize, Option<usize>) {
234 (self.len(), Some(self.len()))
235 }
236}
237
238impl fmt::Debug for dyn Listable + '_ {
239 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
240 struct DebugListable<'a, 'b> {
241 fmt: fmt::DebugList<'a, 'b>,
242 }
243
244 impl Visit for DebugListable<'_, '_> {
245 fn visit_value(&mut self, value: Value<'_>) {
246 self.fmt.entry(&value);
247 }
248 }
249
250 let mut debug = DebugListable {
251 fmt: fmt.debug_list(),
252 };
253
254 self.visit(&mut debug);
255 debug.fmt.finish()
256 }
257}