valuable/mappable.rs
1use crate::*;
2
3use core::fmt;
4
5/// A map-like [`Valuable`] sub-type.
6///
7/// Implemented by [`Valuable`] types that have a map-like shape. This includes
8/// [`HashMap`] and other Rust [collection] types. Values that implement
9/// `Mappable` must return [`Value::Mappable`] from their [`Value::as_value`]
10/// implementation.
11///
12/// [collection]: https://doc.rust-lang.org/stable/std/collections/index.html
13///
14/// # Inspecting
15///
16/// Inspecting `Mappable` entries is done by visiting the value. When visiting a
17/// `Mappable`, contained entries are passed one-by-one to the visitor by
18/// repeatedly calling [`visit_entry()`].
19///
20/// See [`Visit`] documentation for more details.
21///
22/// [`visit_entry()`]: Visit::visit_entry
23/// [`HashMap`]: std::collections::HashMap
24///
25/// # Implementing
26///
27/// Implementing `Mappable` for a custom map type. The map is represented using
28/// a `Vec` of key/value pairs.
29///
30/// ```
31/// use valuable::{Mappable, Valuable, Value, Visit};
32///
33/// struct MyMap<K, V> {
34/// entries: Vec<(K, V)>,
35/// }
36///
37/// impl<K: Valuable, V: Valuable> Valuable for MyMap<K, V> {
38/// fn as_value(&self) -> Value<'_> {
39/// Value::Mappable(self)
40/// }
41///
42/// fn visit(&self, visit: &mut dyn Visit) {
43/// for (k, v) in &self.entries {
44/// visit.visit_entry(k.as_value(), v.as_value());
45/// }
46/// }
47/// }
48///
49/// impl<K: Valuable, V: Valuable> Mappable for MyMap<K, V> {
50/// fn size_hint(&self) -> (usize, Option<usize>) {
51/// let len = self.entries.len();
52/// (len, Some(len))
53/// }
54/// }
55/// ```
56pub trait Mappable: Valuable {
57 /// Returns the bounds on the remaining length of the `Mappable`.
58 ///
59 /// Specifically, `size_hint()` returns a tuple where the first element is
60 /// the lower bound, and the second element is the upper bound.
61 ///
62 /// The second half of the tuple that is returned is an
63 /// [`Option`]`<`[`usize`]`>`. A [`None`] here means that either there is no
64 /// known upper bound, or the upper bound is larger than [`usize`].
65 ///
66 /// # Implementation notes
67 ///
68 /// It is not enforced that a `Mappable` implementation yields the declared
69 /// number of elements. A buggy implementation may yield less than the lower
70 /// bound or more than the upper bound of elements.
71 ///
72 /// `size_hint()` is primarily intended to be used for optimizations such as
73 /// reserving space for the elements of the `Mappable`, but must not be
74 /// trusted to e.g., omit bounds checks in unsafe code. An incorrect
75 /// implementation of `size_hint()` should not lead to memory safety
76 /// violations.
77 ///
78 /// That said, the implementation should provide a correct estimation,
79 /// because otherwise it would be a violation of the trait's protocol.
80 ///
81 /// [`usize`]: type@usize
82 ///
83 /// # Examples
84 ///
85 /// Basic usage:
86 ///
87 /// ```
88 /// use valuable::Mappable;
89 /// use std::collections::HashMap;
90 ///
91 /// let mut map = HashMap::new();
92 /// map.insert("one", 1);
93 /// map.insert("two", 2);
94 /// map.insert("three", 3);
95 ///
96 /// assert_eq!((3, Some(3)), map.size_hint());
97 /// ```
98 fn size_hint(&self) -> (usize, Option<usize>);
99}
100
101macro_rules! deref {
102 (
103 $(
104 $(#[$attrs:meta])*
105 $ty:ty,
106 )*
107 ) => {
108 $(
109 $(#[$attrs])*
110 impl<T: ?Sized + Mappable> Mappable for $ty {
111 fn size_hint(&self) -> (usize, Option<usize>) {
112 T::size_hint(&**self)
113 }
114 }
115 )*
116 };
117}
118
119deref! {
120 &T,
121 &mut T,
122 #[cfg(feature = "alloc")]
123 alloc::boxed::Box<T>,
124 #[cfg(feature = "alloc")]
125 alloc::rc::Rc<T>,
126 #[cfg(not(valuable_no_atomic_cas))]
127 #[cfg(feature = "alloc")]
128 alloc::sync::Arc<T>,
129}
130
131#[cfg(feature = "std")]
132impl<K: Valuable, V: Valuable, S> Valuable for std::collections::HashMap<K, V, S> {
133 fn as_value(&self) -> Value<'_> {
134 Value::Mappable(self)
135 }
136
137 fn visit(&self, visit: &mut dyn Visit) {
138 for (key, value) in self.iter() {
139 visit.visit_entry(key.as_value(), value.as_value());
140 }
141 }
142}
143
144#[cfg(feature = "std")]
145impl<K: Valuable, V: Valuable, S> Mappable for std::collections::HashMap<K, V, S> {
146 fn size_hint(&self) -> (usize, Option<usize>) {
147 self.iter().size_hint()
148 }
149}
150
151#[cfg(feature = "alloc")]
152impl<K: Valuable, V: Valuable> Valuable for alloc::collections::BTreeMap<K, V> {
153 fn as_value(&self) -> Value<'_> {
154 Value::Mappable(self)
155 }
156
157 fn visit(&self, visit: &mut dyn Visit) {
158 for (key, value) in self.iter() {
159 visit.visit_entry(key.as_value(), value.as_value());
160 }
161 }
162}
163
164#[cfg(feature = "alloc")]
165impl<K: Valuable, V: Valuable> Mappable for alloc::collections::BTreeMap<K, V> {
166 fn size_hint(&self) -> (usize, Option<usize>) {
167 self.iter().size_hint()
168 }
169}
170
171impl fmt::Debug for dyn Mappable + '_ {
172 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
173 struct DebugMappable<'a, 'b> {
174 fmt: fmt::DebugMap<'a, 'b>,
175 }
176
177 impl Visit for DebugMappable<'_, '_> {
178 fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) {
179 self.fmt.entry(&key, &value);
180 }
181
182 fn visit_value(&mut self, _: Value<'_>) {}
183 }
184
185 let mut debug = DebugMappable {
186 fmt: fmt.debug_map(),
187 };
188 self.visit(&mut debug);
189 debug.fmt.finish()
190 }
191}