use std::any::TypeId;
use std::marker::PhantomData;
use std::sync::Arc;
#[derive(Clone)]
pub(crate) enum Qualifier {
Static(&'static str),
Dynamic(Arc<str>),
}
impl Qualifier {
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>,
}
impl Clone for TypeKey {
fn clone(&self) -> Self {
Self {
type_id: self.type_id,
type_name: self.type_name,
qualifier: self.qualifier.clone(),
}
}
}
impl PartialEq for TypeKey {
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id && self.qualifier == other.qualifier
}
}
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);
}
}
impl std::fmt::Debug for TypeKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.qualifier {
Some(q) => write!(f, "{}#{q:?}", self.type_name),
None => f.write_str(self.type_name),
}
}
}
impl TypeKey {
pub fn of<T: ?Sized + 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
qualifier: 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)),
}
}
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())),
}
}
pub fn describe(&self) -> String {
match &self.qualifier {
Some(q) => format!("{}#{}", self.type_name, q.as_str()),
None => self.type_name.to_string(),
}
}
}
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);
}
}