use std::any::Any;
use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::sync::{Arc, Weak};
use derive_builder::Builder;
use parking_lot::RwLock;
use weak_table::WeakValueHashMap;
use crate::context::{ContextId, Tree, TreeContext};
use crate::obj_utils::{DynEq, DynHash};
use crate::{span, Span, TreeRoot};
#[derive(Debug, Clone, Builder)]
#[builder(default)]
pub struct Config {
verbose: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for Config {
fn default() -> Self {
Self { verbose: false }
}
}
pub trait Key: Hash + Eq + Debug + Send + Sync + 'static {}
impl<T> Key for T where T: Hash + Eq + Debug + Send + Sync + 'static {}
trait ObjKey: DynHash + DynEq + Debug + Send + Sync + 'static {}
impl<T> ObjKey for T where T: DynHash + DynEq + Debug + Send + Sync + 'static {}
pub trait ToRootSpan {
fn to_root_span(&self) -> Span;
}
impl<T: Display> ToRootSpan for T {
fn to_root_span(&self) -> Span {
span!("{self}")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct AnonymousKey(ContextId);
impl Display for AnonymousKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Anonymous #{}", self.0 .0)
}
}
#[derive(Clone)]
pub struct AnyKey(Arc<dyn ObjKey>);
impl PartialEq for AnyKey {
fn eq(&self, other: &Self) -> bool {
self.0.dyn_eq(other.0.as_dyn_eq())
}
}
impl Eq for AnyKey {}
impl Hash for AnyKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.dyn_hash(state);
}
}
impl Debug for AnyKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl Display for AnyKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
macro_rules! delegate_to_display {
($($t:ty),* $(,)?) => {
$(
if let Some(k) = self.as_any().downcast_ref::<$t>() {
return write!(f, "{}", k);
}
)*
};
}
delegate_to_display!(String, &str, AnonymousKey);
write!(f, "{:?}", self)
}
}
impl AnyKey {
fn new(key: impl ObjKey) -> Self {
Self(Arc::new(key))
}
pub fn as_any(&self) -> &dyn Any {
self.0.as_ref().as_any()
}
pub fn is<K: Any>(&self) -> bool {
self.as_any().is::<K>()
}
pub fn is_anonymous(&self) -> bool {
self.as_any().is::<AnonymousKey>()
}
pub fn downcast_ref<K: Any>(&self) -> Option<&K> {
self.as_any().downcast_ref()
}
}
type Contexts = RwLock<WeakValueHashMap<AnyKey, Weak<TreeContext>>>;
struct RegistryCore {
contexts: Contexts,
config: Config,
}
pub struct Registry(Arc<RegistryCore>);
impl Debug for Registry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Registry")
.field("config", self.config())
.finish_non_exhaustive()
}
}
impl Clone for Registry {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl Registry {
fn contexts(&self) -> &Contexts {
&self.0.contexts
}
fn config(&self) -> &Config {
&self.0.config
}
}
impl Registry {
pub fn new(config: Config) -> Self {
Self(
RegistryCore {
contexts: Default::default(),
config,
}
.into(),
)
}
pub fn try_current() -> Option<Self> {
crate::root::current_registry()
}
pub fn current() -> Self {
Self::try_current().expect("no current registry")
}
fn register_inner(&self, key: impl Key, context: Arc<TreeContext>) -> TreeRoot {
self.contexts()
.write()
.insert(AnyKey::new(key), Arc::clone(&context));
TreeRoot {
context,
registry: WeakRegistry(Arc::downgrade(&self.0)),
}
}
pub fn register(&self, key: impl Key, root_span: impl Into<Span>) -> TreeRoot {
let context = Arc::new(TreeContext::new(root_span.into(), self.config().verbose));
self.register_inner(key, context)
}
pub fn register_derived_root(&self, key: impl Key + ToRootSpan) -> TreeRoot {
let root_span = key.to_root_span();
self.register(key, root_span)
}
pub fn register_anonymous(&self, root_span: impl Into<Span>) -> TreeRoot {
let context = Arc::new(TreeContext::new(root_span.into(), self.config().verbose));
self.register_inner(AnonymousKey(context.id()), context) }
pub fn get(&self, key: impl Key) -> Option<Tree> {
self.contexts()
.read()
.get(&AnyKey::new(key)) .map(|v| v.tree().clone())
}
pub fn clear(&self) {
self.contexts().write().clear();
}
pub fn collect<K: Key + Clone>(&self) -> Vec<(K, Tree)> {
self.contexts()
.read()
.iter()
.filter_map(|(k, v)| {
k.0.as_ref()
.as_any()
.downcast_ref::<K>()
.map(|k| (k.clone(), v.tree().clone()))
})
.collect()
}
pub fn collect_anonymous(&self) -> Vec<Tree> {
self.contexts()
.read()
.iter()
.filter_map(|(k, v)| {
if k.is_anonymous() {
Some(v.tree().clone())
} else {
None
}
})
.collect()
}
pub fn collect_all(&self) -> Vec<(AnyKey, Tree)> {
self.contexts()
.read()
.iter()
.map(|(k, v)| (k.clone(), v.tree().clone()))
.collect()
}
}
pub(crate) struct WeakRegistry(Weak<RegistryCore>);
impl WeakRegistry {
pub fn upgrade(&self) -> Option<Registry> {
self.0.upgrade().map(Registry)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_registry() {
let registry = Registry::new(Config::default());
let _0_i32 = registry.register(0_i32, "0");
let _1_i32 = registry.register(1_i32, "1");
let _2_i32 = registry.register(2_i32, "2");
let _0_str = registry.register("0", "0");
let _1_str = registry.register("1", "1");
let _unit = registry.register((), "()");
let _unit_replaced = registry.register((), "[]");
let _anon = registry.register_anonymous("anon");
let _anon = registry.register_anonymous("anon");
let i32s = registry.collect::<i32>();
assert_eq!(i32s.len(), 3);
let strs = registry.collect::<&'static str>();
assert_eq!(strs.len(), 2);
let units = registry.collect::<()>();
assert_eq!(units.len(), 1);
let anons = registry.collect_anonymous();
assert_eq!(anons.len(), 2);
let all = registry.collect_all();
assert_eq!(all.len(), 8);
}
}