use core::any::Any; use core::borrow::Borrow; use crate::Error; /// No runtime values provided. pub const NO_VALUES: &dyn Values = &(); /// Try to find `key` in `values` and then to convert it to `T`. pub fn get_value(values: &dyn Values, key: impl AsRef) -> Result<&T, Error> { let Some(src) = values.get_value(key.as_ref()) else { return Err(Error::ValueMissing); }; if let Some(value) = src.downcast_ref::() { return Ok(value); } else if let Some(value) = src.downcast_ref::<&T>() { return Ok(value); } #[cfg(feature = "alloc")] if let Some(value) = src.downcast_ref::>() { return Ok(value); } else if let Some(value) = src.downcast_ref::>() { return Ok(value); } else if let Some(value) = src.downcast_ref::>() { return Ok(value); } Err(Error::ValueType) } /// A runtime value store for [`Template::render_with_values()`][crate::Template::render_with_values]. pub trait Values { /// Try to find `key` in this store. fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any>; } crate::impl_for_ref! { impl Values for T { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { T::get_value(self, key) } } } impl Values for () { #[inline] fn get_value<'a>(&'a self, _: &str) -> Option<&'a dyn Any> { None } } impl Values for (K, V) where K: Borrow, V: Value, { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { if self.0.borrow() == key { self.1.ref_any() } else { None } } } impl Values for Option { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { self.as_ref()?.get_value(key) } } impl Values for [(K, V); N] where K: Borrow, V: Value, { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { find_value_linear(self.iter(), key) } } impl Values for [(K, V)] where K: Borrow, V: Value, { fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { find_value_linear(self.iter(), key) } } #[cfg(feature = "alloc")] impl Values for alloc::vec::Vec<(K, V)> where K: Borrow, V: Value, { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { find_value_linear(self.iter(), key) } } #[cfg(feature = "alloc")] impl Values for alloc::collections::VecDeque<(K, V)> where K: Borrow, V: Value, { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { find_value_linear(self.iter(), key) } } #[cfg(feature = "alloc")] impl Values for alloc::collections::LinkedList<(K, V)> where K: Borrow, V: Value, { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { find_value_linear(self.iter(), key) } } fn find_value_linear<'a, K, V, I>(it: I, key: &str) -> Option<&'a dyn Any> where K: Borrow + 'a, V: Value + 'a, I: Iterator, { for (k, v) in it { if k.borrow() == key { return v.ref_any(); } } None } #[cfg(feature = "alloc")] impl Values for alloc::collections::BTreeMap where K: Borrow + core::cmp::Ord, V: Value, { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { self.get(key)?.ref_any() } } #[cfg(feature = "std")] impl Values for std::collections::HashMap where K: Borrow + Eq + core::hash::Hash, V: Value, S: core::hash::BuildHasher, { #[inline] fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> { self.get(key)?.ref_any() } } /// A value in a [`Values`] collection. /// /// This is [dyn](https://doc.rust-lang.org/stable/std/keyword.dyn.html) [Any], /// [Option]<dyn Any>, or a reference to either. pub trait Value { /// Returns a reference to this value unless it is `None`. fn ref_any(&self) -> Option<&dyn Any>; } crate::impl_for_ref! { impl Value for T { #[inline] fn ref_any(&self) -> Option<&dyn Any> { T::ref_any(self) } } } impl Value for dyn Any { #[inline] fn ref_any(&self) -> Option<&dyn Any> { Some(self) } } impl Value for Option { #[inline] fn ref_any(&self) -> Option<&dyn Any> { T::ref_any(self.as_ref()?) } } #[cfg(test)] mod tests { use assert_matches::assert_matches; use super::*; #[track_caller] fn assert_a_10_c_blam(values: &dyn Values) { assert_matches!(get_value::(values, "a"), Ok(10u32)); assert_matches!(get_value::<&str>(values, "c"), Ok(&"blam")); assert_matches!(get_value::(values, "a"), Err(Error::ValueType)); assert_matches!(get_value::(values, "d"), Err(Error::ValueMissing)); } #[track_caller] fn assert_a_12_c_blam(values: &dyn Values) { assert_matches!(get_value::(values, "a"), Ok(12u32)); assert_matches!(get_value::<&str>(values, "c"), Ok(&"blam")); assert_matches!(get_value::(values, "a"), Err(Error::ValueType)); assert_matches!(get_value::(values, "d"), Err(Error::ValueMissing)); } #[cfg(feature = "std")] #[test] fn values_on_hashmap() { use alloc::boxed::Box; use alloc::string::String; use std::collections::HashMap; let mut values: HashMap> = HashMap::new(); values.insert("a".into(), Box::new(12u32)); values.insert("c".into(), Box::new("blam")); assert_a_12_c_blam(&values); let mut values: HashMap<&str, Box> = HashMap::new(); values.insert("a", Box::new(10u32)); assert_matches!(get_value::(&values, "a"), Ok(&10u32)); } #[cfg(feature = "alloc")] #[test] fn values_on_btreemap() { use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::string::String; let mut values: BTreeMap> = BTreeMap::new(); values.insert("a".into(), Box::new(12u32)); values.insert("c".into(), Box::new("blam")); assert_a_12_c_blam(&values); let mut values: BTreeMap<&str, Box> = BTreeMap::new(); values.insert("a", Box::new(10u32)); assert_matches!(get_value::(&values, "a"), Ok(&10u32)); } #[test] fn values_on_slice() { let slice: &[(&str, &dyn Any)] = &[("a", &12u32), ("c", &"blam")]; assert_a_12_c_blam(&slice); } #[cfg(feature = "alloc")] #[test] fn values_vec() { let vec: alloc::vec::Vec<(&str, &dyn Any)> = alloc::vec![("a", &12u32), ("c", &"blam")]; assert_a_12_c_blam(&vec); } #[cfg(feature = "alloc")] #[test] fn values_deque() { let mut deque = alloc::collections::VecDeque::<(&str, &dyn Any)>::new(); deque.push_back(("a", &12u32)); deque.push_back(("c", &"blam")); assert_a_12_c_blam(&deque); deque.pop_front(); deque.push_back(("a", &10u32)); assert_a_10_c_blam(&deque); } #[cfg(feature = "alloc")] #[test] fn values_list() { let mut list = alloc::collections::LinkedList::<(&str, &dyn Any)>::new(); list.push_back(("a", &12u32)); list.push_back(("c", &"blam")); assert_a_12_c_blam(&list); list.pop_front(); list.push_back(("a", &10u32)); assert_a_10_c_blam(&list); } #[test] fn values_on_tuple() { let tuple: (&str, &dyn Any) = ("a", &10u32); assert_matches!(get_value::(&tuple, "a"), Ok(10u32)); assert_matches!(get_value::(&tuple, "a"), Err(Error::ValueType)); assert_matches!(get_value::(&tuple, "b"), Err(Error::ValueMissing)); } }