| use crate::*; |
| |
| use core::fmt; |
| |
| /// A map-like [`Valuable`] sub-type. |
| /// |
| /// Implemented by [`Valuable`] types that have a map-like shape. This includes |
| /// [`HashMap`] and other Rust [collection] types. Values that implement |
| /// `Mappable` must return [`Value::Mappable`] from their [`Value::as_value`] |
| /// implementation. |
| /// |
| /// [collection]: https://doc.rust-lang.org/stable/std/collections/index.html |
| /// |
| /// # Inspecting |
| /// |
| /// Inspecting `Mappable` entries is done by visiting the value. When visiting a |
| /// `Mappable`, contained entries are passed one-by-one to the visitor by |
| /// repeatedly calling [`visit_entry()`]. |
| /// |
| /// See [`Visit`] documentation for more details. |
| /// |
| /// [`visit_entry()`]: Visit::visit_entry |
| /// [`HashMap`]: std::collections::HashMap |
| /// |
| /// # Implementing |
| /// |
| /// Implementing `Mappable` for a custom map type. The map is represented using |
| /// a `Vec` of key/value pairs. |
| /// |
| /// ``` |
| /// use valuable::{Mappable, Valuable, Value, Visit}; |
| /// |
| /// struct MyMap<K, V> { |
| /// entries: Vec<(K, V)>, |
| /// } |
| /// |
| /// impl<K: Valuable, V: Valuable> Valuable for MyMap<K, V> { |
| /// fn as_value(&self) -> Value<'_> { |
| /// Value::Mappable(self) |
| /// } |
| /// |
| /// fn visit(&self, visit: &mut dyn Visit) { |
| /// for (k, v) in &self.entries { |
| /// visit.visit_entry(k.as_value(), v.as_value()); |
| /// } |
| /// } |
| /// } |
| /// |
| /// impl<K: Valuable, V: Valuable> Mappable for MyMap<K, V> { |
| /// fn size_hint(&self) -> (usize, Option<usize>) { |
| /// let len = self.entries.len(); |
| /// (len, Some(len)) |
| /// } |
| /// } |
| /// ``` |
| pub trait Mappable: Valuable { |
| /// Returns the bounds on the remaining length of the `Mappable`. |
| /// |
| /// Specifically, `size_hint()` returns a tuple where the first element is |
| /// the lower bound, and the second element is the upper bound. |
| /// |
| /// The second half of the tuple that is returned is an |
| /// [`Option`]`<`[`usize`]`>`. A [`None`] here means that either there is no |
| /// known upper bound, or the upper bound is larger than [`usize`]. |
| /// |
| /// # Implementation notes |
| /// |
| /// It is not enforced that a `Mappable` implementation yields the declared |
| /// number of elements. A buggy implementation may yield less than the lower |
| /// bound or more than the upper bound of elements. |
| /// |
| /// `size_hint()` is primarily intended to be used for optimizations such as |
| /// reserving space for the elements of the `Mappable`, but must not be |
| /// trusted to e.g., omit bounds checks in unsafe code. An incorrect |
| /// implementation of `size_hint()` should not lead to memory safety |
| /// violations. |
| /// |
| /// That said, the implementation should provide a correct estimation, |
| /// because otherwise it would be a violation of the trait's protocol. |
| /// |
| /// [`usize`]: type@usize |
| /// |
| /// # Examples |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// use valuable::Mappable; |
| /// use std::collections::HashMap; |
| /// |
| /// let mut map = HashMap::new(); |
| /// map.insert("one", 1); |
| /// map.insert("two", 2); |
| /// map.insert("three", 3); |
| /// |
| /// assert_eq!((3, Some(3)), map.size_hint()); |
| /// ``` |
| fn size_hint(&self) -> (usize, Option<usize>); |
| } |
| |
| macro_rules! deref { |
| ( |
| $( |
| $(#[$attrs:meta])* |
| $ty:ty, |
| )* |
| ) => { |
| $( |
| $(#[$attrs])* |
| impl<T: ?Sized + Mappable> Mappable for $ty { |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| T::size_hint(&**self) |
| } |
| } |
| )* |
| }; |
| } |
| |
| deref! { |
| &T, |
| &mut T, |
| #[cfg(feature = "alloc")] |
| alloc::boxed::Box<T>, |
| #[cfg(feature = "alloc")] |
| alloc::rc::Rc<T>, |
| #[cfg(not(valuable_no_atomic_cas))] |
| #[cfg(feature = "alloc")] |
| alloc::sync::Arc<T>, |
| } |
| |
| #[cfg(feature = "std")] |
| impl<K: Valuable, V: Valuable> Valuable for std::collections::HashMap<K, V> { |
| fn as_value(&self) -> Value<'_> { |
| Value::Mappable(self) |
| } |
| |
| fn visit(&self, visit: &mut dyn Visit) { |
| for (key, value) in self.iter() { |
| visit.visit_entry(key.as_value(), value.as_value()); |
| } |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl<K: Valuable, V: Valuable> Mappable for std::collections::HashMap<K, V> { |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| self.iter().size_hint() |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl<K: Valuable, V: Valuable> Valuable for alloc::collections::BTreeMap<K, V> { |
| fn as_value(&self) -> Value<'_> { |
| Value::Mappable(self) |
| } |
| |
| fn visit(&self, visit: &mut dyn Visit) { |
| for (key, value) in self.iter() { |
| visit.visit_entry(key.as_value(), value.as_value()); |
| } |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl<K: Valuable, V: Valuable> Mappable for alloc::collections::BTreeMap<K, V> { |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| self.iter().size_hint() |
| } |
| } |
| |
| impl fmt::Debug for dyn Mappable + '_ { |
| fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| struct DebugMappable<'a, 'b> { |
| fmt: fmt::DebugMap<'a, 'b>, |
| } |
| |
| impl Visit for DebugMappable<'_, '_> { |
| fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { |
| self.fmt.entry(&key, &value); |
| } |
| |
| fn visit_value(&mut self, _: Value<'_>) {} |
| } |
| |
| let mut debug = DebugMappable { |
| fmt: fmt.debug_map(), |
| }; |
| self.visit(&mut debug); |
| debug.fmt.finish() |
| } |
| } |