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}