1use std::any::TypeId;
2use std::fmt::{Debug, Display, Formatter};
3use std::hash::{Hash, Hasher};
4
5#[derive(Clone)]
8pub enum ServiceKey {
9 Type(TypeId),
11
12 Named { type_id: TypeId, name: String },
14}
15
16impl ServiceKey {
17 pub fn of_type<T: 'static>() -> Self {
19 ServiceKey::Type(TypeId::of::<T>())
20 }
21
22 pub fn named<T: 'static>(name: impl Into<String>) -> Self {
24 ServiceKey::Named {
25 type_id: TypeId::of::<T>(),
26 name: name.into(),
27 }
28 }
29
30 pub fn type_id(&self) -> TypeId {
32 match self {
33 ServiceKey::Type(type_id) => *type_id,
34 ServiceKey::Named { type_id, .. } => *type_id,
35 }
36 }
37
38 pub fn name(&self) -> Option<&str> {
40 match self {
41 ServiceKey::Type(_) => None,
42 ServiceKey::Named { name, .. } => Some(name),
43 }
44 }
45
46 pub fn is_named(&self) -> bool {
48 matches!(self, ServiceKey::Named { .. })
49 }
50
51 pub fn type_name(&self) -> &'static str {
53 match self {
54 ServiceKey::Type(_type_id) => {
55 "Unknown"
57 }
58 ServiceKey::Named { .. } => "Unknown",
59 }
60 }
61}
62
63impl PartialEq for ServiceKey {
64 fn eq(&self, other: &Self) -> bool {
65 match (self, other) {
66 (ServiceKey::Type(a), ServiceKey::Type(b)) => a == b,
67 (
68 ServiceKey::Named {
69 type_id: a_type,
70 name: a_name,
71 },
72 ServiceKey::Named {
73 type_id: b_type,
74 name: b_name,
75 },
76 ) => a_type == b_type && a_name == b_name,
77 _ => false,
78 }
79 }
80}
81
82impl Eq for ServiceKey {}
83
84impl Hash for ServiceKey {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 match self {
87 ServiceKey::Type(type_id) => {
88 0u8.hash(state); type_id.hash(state);
90 }
91 ServiceKey::Named { type_id, name } => {
92 1u8.hash(state); type_id.hash(state);
94 name.hash(state);
95 }
96 }
97 }
98}
99
100impl Debug for ServiceKey {
101 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
102 match self {
103 ServiceKey::Type(type_id) => {
104 write!(f, "ServiceKey::Type({type_id:?})")
105 }
106 ServiceKey::Named { type_id, name } => {
107 write!(f, "ServiceKey::Named(type: {type_id:?}, name: '{name}')")
108 }
109 }
110 }
111}
112
113impl Display for ServiceKey {
114 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115 match self {
116 ServiceKey::Type(_) => {
117 write!(f, "Type Service")
118 }
119 ServiceKey::Named { name, .. } => {
120 write!(f, "Named Service '{name}'")
121 }
122 }
123 }
124}
125
126#[macro_export]
128macro_rules! service_key {
129 ($type:ty) => {
130 $crate::ServiceKey::of_type::<$type>()
131 };
132 ($type:ty, $name:expr) => {
133 $crate::ServiceKey::named::<$type>($name)
134 };
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 struct TestService;
142 struct AnotherService;
143
144 #[test]
145 fn test_type_key_creation() {
146 let key1 = ServiceKey::of_type::<TestService>();
147 let key2 = ServiceKey::of_type::<TestService>();
148 let key3 = ServiceKey::of_type::<AnotherService>();
149
150 assert_eq!(key1, key2);
151 assert_ne!(key1, key3);
152 assert!(!key1.is_named());
153 assert!(key1.name().is_none());
154 }
155
156 #[test]
157 fn test_named_key_creation() {
158 let key1 = ServiceKey::named::<TestService>("primary");
159 let key2 = ServiceKey::named::<TestService>("primary");
160 let key3 = ServiceKey::named::<TestService>("secondary");
161 let key4 = ServiceKey::named::<AnotherService>("primary");
162
163 assert_eq!(key1, key2);
164 assert_ne!(key1, key3);
165 assert_ne!(key1, key4);
166 assert!(key1.is_named());
167 assert_eq!(key1.name(), Some("primary"));
168 }
169
170 #[test]
171 fn test_type_vs_named_key() {
172 let type_key = ServiceKey::of_type::<TestService>();
173 let named_key = ServiceKey::named::<TestService>("test");
174
175 assert_ne!(type_key, named_key);
176 assert_eq!(type_key.type_id(), named_key.type_id());
177 }
178
179 #[test]
180 fn test_macro() {
181 let key1 = service_key!(TestService);
182 let key2 = service_key!(TestService, "named");
183
184 assert_eq!(key1, ServiceKey::of_type::<TestService>());
185 assert_eq!(key2, ServiceKey::named::<TestService>("named"));
186 }
187}