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 { /// entries: Vec<(K, V)>, /// } /// /// impl Valuable for MyMap { /// 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 Mappable for MyMap { /// fn size_hint(&self) -> (usize, Option) { /// 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); } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Mappable for $ty { fn size_hint(&self) -> (usize, Option) { T::size_hint(&**self) } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } #[cfg(feature = "std")] impl Valuable for std::collections::HashMap { 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 Mappable for std::collections::HashMap { fn size_hint(&self) -> (usize, Option) { self.iter().size_hint() } } #[cfg(feature = "alloc")] impl Valuable for alloc::collections::BTreeMap { 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 Mappable for alloc::collections::BTreeMap { fn size_hint(&self) -> (usize, Option) { 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() } }