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}