use std::{
any::Any,
borrow::Cow,
collections::{HashMap, hash_map::DefaultHasher},
fmt,
hash::{Hash, Hasher},
sync::Arc,
};
trait DynAttributeValue: Send + Sync {
fn value_any(&self) -> &dyn Any;
fn eq_dyn(&self, other: &dyn DynAttributeValue) -> bool;
fn hash_dyn(&self, state: &mut dyn Hasher);
fn fmt_dyn(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
struct NamedAttributeValue<T>(T);
impl<T> DynAttributeValue for NamedAttributeValue<T>
where
T: Any + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static,
{
fn value_any(&self) -> &dyn Any {
&self.0
}
fn eq_dyn(&self, other: &dyn DynAttributeValue) -> bool {
other
.value_any()
.downcast_ref::<T>()
.is_some_and(|other| self.0 == *other)
}
fn hash_dyn(&self, state: &mut dyn Hasher) {
let mut hasher = DefaultHasher::new();
self.0.hash(&mut hasher);
state.write_u64(hasher.finish());
}
fn fmt_dyn(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Default)]
pub struct Attributes(HashMap<Cow<'static, str>, Arc<dyn DynAttributeValue>>);
impl Clone for Attributes {
fn clone(&self) -> Self {
Self(
self.0
.iter()
.map(|(key, value)| (key.clone(), value.clone()))
.collect(),
)
}
}
impl Attributes {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn insert<T>(&mut self, key: impl Into<Cow<'static, str>>, value: T)
where
T: Any + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static,
{
self.0
.insert(key.into(), Arc::new(NamedAttributeValue(value)));
}
pub fn extend(&mut self, other: Self) {
self.0.extend(other.0);
}
pub fn remove(&mut self, key: &str) -> bool {
self.0.remove(key).is_some()
}
pub fn contains_key(&self, key: &str) -> bool {
self.0.contains_key(key)
}
pub fn get_named(&self, key: &str) -> Option<&dyn Any> {
self.0.get(key).map(|value| value.value_any())
}
pub fn get_named_as<T: Any + 'static>(&self, key: &str) -> Option<&T> {
self.0
.get(key)
.and_then(|value| value.value_any().downcast_ref::<T>())
}
}
impl PartialEq for Attributes {
fn eq(&self, other: &Self) -> bool {
self.0.len() == other.0.len()
&& self.0.iter().all(|(key, value)| {
other
.0
.get(key)
.is_some_and(|other| value.eq_dyn(other.as_ref()))
})
}
}
impl Eq for Attributes {}
impl Hash for Attributes {
fn hash<H: Hasher>(&self, state: &mut H) {
let mut entries = self
.0
.iter()
.map(|(key, value)| {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
value.hash_dyn(&mut hasher);
hasher.finish()
})
.collect::<Vec<_>>();
entries.sort_unstable();
self.0.len().hash(state);
entries.hash(state);
}
}
impl fmt::Debug for Attributes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut entries = self.0.iter().collect::<Vec<_>>();
entries.sort_by_key(|(left, _)| *left);
let mut map = f.debug_map();
for (key, value) in entries {
map.entry(key, &fmt::from_fn(|f| value.fmt_dyn(f)));
}
map.finish()
}
}