use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hasher};
use std::sync::Arc;
#[derive(Debug, Clone, Default)]
pub struct Extensions {
inner: HashMap<TypeId, Arc<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>,
}
impl Extensions {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn insert<T: Any + Send + Sync>(&mut self, value: T) -> Option<Arc<T>> {
self.insert_arc(Arc::new(value))
}
pub fn insert_arc<T: Any + Send + Sync>(&mut self, value: Arc<T>) -> Option<Arc<T>> {
self.inner
.insert(TypeId::of::<T>(), value)
.map(|p| Arc::downcast::<T>(p).expect("TypeId matches T"))
}
#[deprecated(
since = "54.0.0",
note = "use `insert` or `insert_arc`; only retained to support the deprecated `PartitionedFile::with_extensions` shim"
)]
pub fn insert_dyn(
&mut self,
value: Arc<dyn Any + Send + Sync>,
) -> Option<Arc<dyn Any + Send + Sync>> {
let id = (*value).type_id();
self.inner.insert(id, value)
}
pub fn get<T: Any + Send + Sync>(&self) -> Option<&T> {
self.inner
.get(&TypeId::of::<T>())
.and_then(|a| a.downcast_ref::<T>())
}
pub fn get_arc<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
self.inner
.get(&TypeId::of::<T>())
.map(|a| Arc::downcast::<T>(Arc::clone(a)).expect("TypeId matches T"))
}
pub fn contains<T: Any + Send + Sync>(&self) -> bool {
self.inner.contains_key(&TypeId::of::<T>())
}
pub fn merge(&mut self, other: &Extensions) {
for (id, ext) in &other.inner {
self.inner.insert(*id, Arc::clone(ext));
}
}
}
#[derive(Default)]
struct IdHasher(u64);
impl Hasher for IdHasher {
fn write(&mut self, _: &[u8]) {
unreachable!("TypeId calls write_u64");
}
#[inline]
fn write_u64(&mut self, id: u64) {
self.0 = id;
}
#[inline]
fn finish(&self) -> u64 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct A(u32);
#[derive(Debug, PartialEq)]
struct B(&'static str);
#[test]
fn insert_get_replace() {
let mut ext = Extensions::new();
assert!(ext.is_empty());
ext.insert(A(1));
ext.insert_arc(Arc::new(B("x")));
assert_eq!(ext.len(), 2);
assert_eq!(ext.get::<A>(), Some(&A(1)));
assert_eq!(ext.get::<B>(), Some(&B("x")));
assert!(ext.contains::<A>());
let prev = ext.insert(A(2));
assert_eq!(prev.as_deref(), Some(&A(1)));
assert_eq!(ext.get::<A>(), Some(&A(2)));
}
#[test]
#[expect(deprecated)]
fn insert_dyn_keys_by_concrete_type() {
let mut ext = Extensions::new();
let erased: Arc<dyn Any + Send + Sync> = Arc::new(A(7));
ext.insert_dyn(erased);
assert_eq!(ext.get::<A>(), Some(&A(7)));
}
#[test]
fn merge_other_wins() {
let mut a = Extensions::new();
a.insert(A(1));
let mut b = Extensions::new();
b.insert(A(2));
b.insert(B("hi"));
a.merge(&b);
assert_eq!(a.get::<A>(), Some(&A(2)));
assert_eq!(a.get::<B>(), Some(&B("hi")));
}
}