use std::any::TypeId;
use std::marker::PhantomData;
use std::num::NonZeroU64;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct InstanceId(NonZeroU64);
impl InstanceId {
pub(crate) fn allocate() -> Self {
static NEXT: AtomicU64 = AtomicU64::new(1);
let value = NEXT
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |n| n.checked_add(1))
.expect("rutis InstanceId exhausted");
Self(NonZeroU64::new(value).expect("InstanceId starts at one"))
}
}
impl std::fmt::Display for InstanceId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone)]
pub(crate) enum Qualifier {
Static(&'static str),
Dynamic(Arc<str>),
}
impl Qualifier {
pub(crate) fn as_str(&self) -> &str {
match self {
Qualifier::Static(s) => s,
Qualifier::Dynamic(s) => s,
}
}
}
impl std::fmt::Debug for Qualifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self.as_str(), f)
}
}
impl PartialEq for Qualifier {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl Eq for Qualifier {}
impl std::hash::Hash for Qualifier {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
pub struct TypeKey {
type_id: TypeId,
type_name: &'static str,
qualifier: Option<Qualifier>,
instance: Option<InstanceId>,
}
impl Clone for TypeKey {
fn clone(&self) -> Self {
Self {
type_id: self.type_id,
type_name: self.type_name,
qualifier: self.qualifier.clone(),
instance: self.instance,
}
}
}
impl PartialEq for TypeKey {
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id
&& self.qualifier == other.qualifier
&& self.instance == other.instance
}
}
impl Eq for TypeKey {}
impl std::hash::Hash for TypeKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.type_id.hash(state);
self.qualifier.hash(state);
self.instance.hash(state);
}
}
impl std::fmt::Debug for TypeKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.describe())
}
}
impl TypeKey {
pub(crate) fn type_id(&self) -> TypeId {
self.type_id
}
pub fn name(&self) -> Option<&str> {
self.qualifier.as_ref().map(Qualifier::as_str)
}
pub(crate) fn has_type<T: ?Sized + 'static>(&self) -> bool {
self.type_id == TypeId::of::<T>()
}
pub(crate) fn type_name(&self) -> &'static str {
self.type_name
}
pub fn of<T: ?Sized + 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
qualifier: None,
instance: None,
}
}
pub fn keyed<T: ?Sized + 'static>(qualifier: &'static str) -> Self {
Self {
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
qualifier: Some(Qualifier::Static(qualifier)),
instance: None,
}
}
pub fn keyed_dynamic<T: ?Sized + 'static>(name: impl Into<Arc<str>>) -> Self {
Self {
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
qualifier: Some(Qualifier::Dynamic(name.into())),
instance: None,
}
}
pub fn instance<T: ?Sized + 'static>(id: InstanceId) -> Self {
Self::of::<T>().with_instance(id)
}
pub fn with_instance(mut self, id: InstanceId) -> Self {
self.instance = Some(id);
self
}
pub fn instance_id(&self) -> Option<InstanceId> {
self.instance
}
pub fn describe(&self) -> String {
let base = match &self.qualifier {
Some(q) => format!("{}#{}", self.type_name, q.as_str()),
None => self.type_name.to_string(),
};
match self.instance {
Some(id) => format!("{base}@{id}"),
None => base,
}
}
}
pub struct EventKey<E: crate::Event> {
qualifier: Option<Qualifier>,
instance: Option<InstanceId>,
marker: PhantomData<fn(E) -> E>,
}
impl<E: crate::Event> EventKey<E> {
pub const fn of() -> Self {
Self {
qualifier: None,
instance: None,
marker: PhantomData,
}
}
pub const fn named(name: &'static str) -> Self {
Self {
qualifier: Some(Qualifier::Static(name)),
instance: None,
marker: PhantomData,
}
}
pub fn dynamic(name: impl Into<Arc<str>>) -> Self {
Self {
qualifier: Some(Qualifier::Dynamic(name.into())),
instance: None,
marker: PhantomData,
}
}
pub const fn instance(mut self, id: InstanceId) -> Self {
self.instance = Some(id);
self
}
pub fn name(&self) -> Option<&str> {
self.qualifier.as_ref().map(Qualifier::as_str)
}
pub fn instance_id(&self) -> Option<InstanceId> {
self.instance
}
pub fn describe(&self) -> String {
self.erased().describe()
}
pub(crate) fn erased(&self) -> TypeKey {
TypeKey {
type_id: TypeId::of::<E>(),
type_name: std::any::type_name::<E>(),
qualifier: self.qualifier.clone(),
instance: self.instance,
}
}
pub(crate) fn from_erased(key: &TypeKey) -> Self {
debug_assert!(key.has_type::<E>());
Self {
qualifier: key.qualifier.clone(),
instance: key.instance,
marker: PhantomData,
}
}
}
impl<E: crate::Event> Default for EventKey<E> {
fn default() -> Self {
Self::of()
}
}
impl<E: crate::Event> Clone for EventKey<E> {
fn clone(&self) -> Self {
Self {
qualifier: self.qualifier.clone(),
instance: self.instance,
marker: PhantomData,
}
}
}
impl<E: crate::Event> PartialEq for EventKey<E> {
fn eq(&self, other: &Self) -> bool {
self.qualifier == other.qualifier && self.instance == other.instance
}
}
impl<E: crate::Event> Eq for EventKey<E> {}
impl<E: crate::Event> std::hash::Hash for EventKey<E> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.erased().hash(state);
}
}
impl<E: crate::Event> std::fmt::Debug for EventKey<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.erased().fmt(f)
}
}
pub struct EventPattern<E: crate::Event> {
prefixes: Vec<Arc<str>>,
marker: PhantomData<fn(E) -> E>,
}
impl<E: crate::Event> EventPattern<E> {
pub fn prefix(prefix: impl Into<Arc<str>>) -> Self {
Self::any_prefix([prefix])
}
pub fn any_prefix<S: Into<Arc<str>>>(prefixes: impl IntoIterator<Item = S>) -> Self {
Self {
prefixes: prefixes.into_iter().map(Into::into).collect(),
marker: PhantomData,
}
}
pub fn prefixes(&self) -> &[Arc<str>] {
&self.prefixes
}
pub(crate) fn into_prefixes(self) -> Vec<Arc<str>> {
self.prefixes
}
}
impl<E: crate::Event> Clone for EventPattern<E> {
fn clone(&self) -> Self {
Self {
prefixes: self.prefixes.clone(),
marker: PhantomData,
}
}
}
impl<E: crate::Event> std::fmt::Debug for EventPattern<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventPattern")
.field("type", &std::any::type_name::<E>())
.field("prefixes", &self.prefixes)
.finish()
}
}
pub type ServiceKey = TypeKey;
pub struct Key<T: ?Sized + 'static> {
name: &'static str,
_marker: PhantomData<fn() -> T>,
}
impl<T: ?Sized + 'static> Key<T> {
pub const fn new(name: &'static str) -> Self {
Self {
name,
_marker: PhantomData,
}
}
}
impl<T: ?Sized + 'static> From<Key<T>> for TypeKey {
fn from(k: Key<T>) -> Self {
TypeKey::keyed::<T>(k.name)
}
}
pub(crate) type ScopeId = std::sync::Arc<str>;
#[cfg(test)]
mod describe_tests {
use super::*;
struct ReadableService;
#[test]
fn describe_uses_type_name_and_qualifier() {
let plain = TypeKey::of::<ReadableService>();
assert!(plain.describe().contains("ReadableService"));
let keyed = TypeKey::keyed_dynamic::<ReadableService>("session-1/main");
assert_eq!(
keyed.describe(),
format!(
"{}#session-1/main",
std::any::type_name::<ReadableService>()
)
);
assert_eq!(plain, TypeKey::of::<ReadableService>());
assert_ne!(plain, keyed);
}
#[test]
fn instance_is_part_of_key_equality_hash_and_description() {
use std::collections::HashSet;
let one = InstanceId::allocate();
let two = InstanceId::allocate();
assert_ne!(one, two);
let a = TypeKey::keyed::<ReadableService>("main").with_instance(one);
let same = TypeKey::keyed_dynamic::<ReadableService>("main").with_instance(one);
let other = TypeKey::keyed::<ReadableService>("main").with_instance(two);
assert_eq!(a, same);
assert_ne!(a, other);
assert_ne!(a, TypeKey::keyed::<ReadableService>("main"));
assert_eq!(
a.describe(),
format!("{}#main@{one}", std::any::type_name::<ReadableService>())
);
let mut keys = HashSet::new();
keys.insert(a);
assert!(keys.contains(&same));
assert!(!keys.contains(&other));
}
}