use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use std::net::SocketAddr; use std::sync::Arc;
#[derive(Default, Clone)]
pub struct Metadata {
inner: Arc<tokio::sync::RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>>,
}
impl Metadata {
pub fn new() -> Self {
Self::default()
}
pub async fn insert_typed<T: Any + Send + Sync>(
&self, value: T,
) -> Option<Arc<dyn Any + Send + Sync>> {
let mut map = self.inner.write().await; map.insert(TypeId::of::<T>(), Arc::new(value))
}
pub async fn get<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
let map = self.inner.read().await; map.get(&TypeId::of::<T>()).and_then(|arc_any| {
arc_any.clone().downcast::<T>().ok()
})
}
pub async fn contains<T: Any + Send + Sync>(&self) -> bool {
let map = self.inner.read().await;
map.contains_key(&TypeId::of::<T>())
}
pub async fn remove<T: Any + Send + Sync>(&self) -> Option<Arc<dyn Any + Send + Sync>> {
let mut map = self.inner.write().await;
map.remove(&TypeId::of::<T>())
}
pub async fn is_empty(&self) -> bool {
self.inner.read().await.is_empty()
}
pub async fn len(&self) -> usize {
self.inner.read().await.len()
}
}
impl fmt::Debug for Metadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Metadata").finish_non_exhaustive()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerAddress(pub SocketAddr);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ZapUserId(pub String);
#[cfg(test)]
mod additional_metadata_tests {
use super::*;
use std::net::SocketAddr;
#[tokio::test]
async fn test_metadata_concurrent_type_safety() {
let metadata = Metadata::new();
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
metadata.insert_typed(PeerAddress(addr)).await;
let meta_clone1 = metadata.clone();
let writer_task = tokio::spawn(async move {
for i in 0..100 {
meta_clone1
.insert_typed(ZapUserId(format!("user-{}", i)))
.await;
tokio::task::yield_now().await;
}
});
let meta_clone2 = metadata.clone();
let reader_task = tokio::spawn(async move {
for _ in 0..100 {
let addr_opt = meta_clone2.get::<PeerAddress>().await;
assert!(addr_opt.is_some(), "PeerAddress should always be present");
let _user_opt = meta_clone2.get::<ZapUserId>().await;
tokio::task::yield_now().await;
}
});
let (res_w, res_r) = tokio::join!(writer_task, reader_task);
assert!(res_w.is_ok());
assert!(res_r.is_ok());
}
}