ferrous_di/key.rs
1//! Service key types for the dependency injection container.
2
3use std::any::TypeId;
4
5/// Key for service storage and lookup.
6///
7/// Keys uniquely identify services in the container, supporting both
8/// unnamed and named service registrations. Each key type serves a
9/// specific purpose in the DI system's service resolution mechanism.
10///
11/// # Key Types
12///
13/// - **Type**: Concrete types (structs, enums, primitives)
14/// - **Trait**: Single trait implementations
15/// - **MultiTrait**: Multiple trait implementations with indexing
16/// - **Named variants**: All above with additional string names
17///
18/// # Examples
19///
20/// ```rust
21/// use ferrous_di::{ServiceCollection, Resolver, Key};
22/// use std::sync::Arc;
23///
24/// // Concrete type keys
25/// let mut services = ServiceCollection::new();
26/// services.add_singleton(42u32);
27/// services.add_named_singleton("config_port", 8080u32);
28///
29/// // Trait keys
30/// trait Logger: Send + Sync {
31/// fn log(&self, msg: &str);
32/// }
33///
34/// struct ConsoleLogger;
35/// impl Logger for ConsoleLogger {
36/// fn log(&self, msg: &str) {
37/// println!("LOG: {}", msg);
38/// }
39/// }
40///
41/// services.add_singleton_trait(Arc::new(ConsoleLogger) as Arc<dyn Logger>);
42///
43/// let provider = services.build();
44///
45/// // Resolution uses keys internally
46/// let number = provider.get_required::<u32>(); // Uses Type key
47/// let port = provider.get_named_required::<u32>("config_port"); // Uses TypeNamed key
48/// let logger = provider.get_required_trait::<dyn Logger>(); // Uses Trait key
49///
50/// assert_eq!(*number, 42);
51/// assert_eq!(*port, 8080);
52/// logger.log("Service resolution successful");
53/// ```
54#[derive(Debug, Clone)]
55pub enum Key {
56 /// Concrete type key with TypeId and name for diagnostics
57 ///
58 /// Used for registering and resolving concrete types like `String`,
59 /// `Database`, custom structs, etc. The TypeId provides fast lookup
60 /// while the name helps with debugging.
61 Type(TypeId, &'static str),
62 /// Single trait binding key
63 ///
64 /// Used for registering and resolving trait objects like `dyn Logger`.
65 /// Only stores the trait name since traits don't have TypeId.
66 Trait(&'static str),
67 /// Multi-trait binding with index
68 ///
69 /// Used when multiple implementations are registered for the same trait.
70 /// The index distinguishes between different implementations.
71 MultiTrait(&'static str, usize),
72
73 // Named service variants
74 /// Named concrete type key with TypeId, typename, and name
75 ///
76 /// Like `Type` but with an additional string name for cases where
77 /// multiple instances of the same type need different registrations.
78 TypeNamed(TypeId, &'static str, &'static str),
79 /// Named single trait binding key with trait name and service name
80 ///
81 /// Like `Trait` but with an additional string name for different
82 /// implementations of the same trait.
83 TraitNamed(&'static str, &'static str),
84 /// Named multi-trait binding with trait name, service name, and index
85 ///
86 /// Combination of `MultiTrait` and naming for complex scenarios with
87 /// multiple named implementations of the same trait.
88 MultiTraitNamed(&'static str, &'static str, usize),
89}
90
91impl Key {
92 /// Get the type or trait name for display
93 ///
94 /// Returns the human-readable type or trait name for debugging and
95 /// error messages. This is the `std::any::type_name` result.
96 ///
97 /// # Examples
98 ///
99 /// ```rust
100 /// use ferrous_di::Key;
101 /// use std::any::TypeId;
102 ///
103 /// let type_key = Key::Type(TypeId::of::<String>(), "alloc::string::String");
104 /// assert_eq!(type_key.display_name(), "alloc::string::String");
105 ///
106 /// let trait_key = Key::Trait("dyn core::fmt::Debug");
107 /// assert_eq!(trait_key.display_name(), "dyn core::fmt::Debug");
108 ///
109 /// let named_key = Key::TypeNamed(TypeId::of::<u32>(), "u32", "port");
110 /// assert_eq!(named_key.display_name(), "u32");
111 /// ```
112 pub fn display_name(&self) -> &'static str {
113 match self {
114 Key::Type(_, name) => name,
115 Key::Trait(name) => name,
116 Key::MultiTrait(name, _) => name,
117 Key::TypeNamed(_, name, _) => name,
118 Key::TraitNamed(name, _) => name,
119 Key::MultiTraitNamed(name, _, _) => name,
120 }
121 }
122
123 /// Get the service name for named services, or None for unnamed services
124 ///
125 /// Returns the service name for keys that represent named service
126 /// registrations, or `None` for unnamed services.
127 ///
128 /// # Examples
129 ///
130 /// ```rust
131 /// use ferrous_di::Key;
132 /// use std::any::TypeId;
133 ///
134 /// // Unnamed services return None
135 /// let unnamed_key = Key::Type(TypeId::of::<String>(), "alloc::string::String");
136 /// assert_eq!(unnamed_key.service_name(), None);
137 ///
138 /// let trait_key = Key::Trait("dyn core::fmt::Debug");
139 /// assert_eq!(trait_key.service_name(), None);
140 ///
141 /// // Named services return Some(name)
142 /// let named_type = Key::TypeNamed(TypeId::of::<u32>(), "u32", "database_port");
143 /// assert_eq!(named_type.service_name(), Some("database_port"));
144 ///
145 /// let named_trait = Key::TraitNamed("dyn myapp::Logger", "console_logger");
146 /// assert_eq!(named_trait.service_name(), Some("console_logger"));
147 /// ```
148 pub fn service_name(&self) -> Option<&'static str> {
149 match self {
150 Key::Type(_, _) | Key::Trait(_) | Key::MultiTrait(_, _) => None,
151 Key::TypeNamed(_, _, name) => Some(name),
152 Key::TraitNamed(_, name) => Some(name),
153 Key::MultiTraitNamed(_, name, _) => Some(name),
154 }
155 }
156}
157
158// Ultra-optimized equality for hot path: TypeId-only comparison for concrete types
159impl PartialEq for Key {
160 #[inline(always)]
161 fn eq(&self, other: &Self) -> bool {
162 match (self, other) {
163 // Hot path: TypeId comparison only (ignore string for performance)
164 (Key::Type(a, _), Key::Type(b, _)) => a == b,
165 (Key::TypeNamed(a, _, name_a), Key::TypeNamed(b, _, name_b)) => a == b && name_a == name_b,
166
167 // Multi-bindings and traits (less common)
168 (Key::Trait(a), Key::Trait(b)) => a == b,
169 (Key::TraitNamed(a, name_a), Key::TraitNamed(b, name_b)) => a == b && name_a == name_b,
170 (Key::MultiTrait(a, idx_a), Key::MultiTrait(b, idx_b)) => a == b && idx_a == idx_b,
171 (Key::MultiTraitNamed(a, name_a, idx_a), Key::MultiTraitNamed(b, name_b, idx_b)) => {
172 a == b && name_a == name_b && idx_a == idx_b
173 }
174
175 // Different variants never equal
176 _ => false
177 }
178 }
179}
180
181impl Eq for Key {}
182
183// Ordering for sorting in hybrid registry
184impl PartialOrd for Key {
185 #[inline(always)]
186 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
187 Some(self.cmp(other))
188 }
189}
190
191impl Ord for Key {
192 #[inline(always)]
193 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
194 use std::cmp::Ordering;
195
196 match (self, other) {
197 // Compare by TypeId for concrete types (most common)
198 (Key::Type(a, _), Key::Type(b, _)) => a.cmp(b),
199 (Key::TypeNamed(a, _, name_a), Key::TypeNamed(b, _, name_b)) => {
200 a.cmp(b).then_with(|| name_a.cmp(name_b))
201 }
202
203 // Different variants - order by variant type
204 (Key::Type(_, _), _) => Ordering::Less,
205 (_, Key::Type(_, _)) => Ordering::Greater,
206 (Key::TypeNamed(_, _, _), _) => Ordering::Less,
207 (_, Key::TypeNamed(_, _, _)) => Ordering::Greater,
208
209 // Handle remaining variants
210 (Key::Trait(a), Key::Trait(b)) => a.cmp(b),
211 (Key::TraitNamed(a, name_a), Key::TraitNamed(b, name_b)) => {
212 a.cmp(b).then_with(|| name_a.cmp(name_b))
213 }
214 (Key::MultiTrait(a, idx_a), Key::MultiTrait(b, idx_b)) => {
215 a.cmp(b).then_with(|| idx_a.cmp(idx_b))
216 }
217 (Key::MultiTraitNamed(a, name_a, idx_a), Key::MultiTraitNamed(b, name_b, idx_b)) => {
218 a.cmp(b).then_with(|| name_a.cmp(name_b)).then_with(|| idx_a.cmp(idx_b))
219 }
220
221 // All other cases use ordering based on variant position
222 _ => Ordering::Equal, // Should not reach here with exhaustive match
223 }
224 }
225}
226
227// Ultra-optimized hash for hot path: TypeId-only hash for concrete types
228impl std::hash::Hash for Key {
229 #[inline(always)]
230 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
231 match self {
232 // Hot path: Hash TypeId only (ignore string for performance)
233 Key::Type(id, _) => {
234 0u8.hash(state); // Discriminant
235 id.hash(state);
236 }
237 Key::TypeNamed(id, _, name) => {
238 1u8.hash(state);
239 id.hash(state);
240 name.hash(state);
241 }
242
243 // Multi-bindings and traits (less common)
244 Key::Trait(name) => {
245 2u8.hash(state);
246 name.hash(state);
247 }
248 Key::TraitNamed(name, named) => {
249 3u8.hash(state);
250 name.hash(state);
251 named.hash(state);
252 }
253 Key::MultiTrait(name, idx) => {
254 4u8.hash(state);
255 name.hash(state);
256 idx.hash(state);
257 }
258 Key::MultiTraitNamed(name, named, idx) => {
259 5u8.hash(state);
260 name.hash(state);
261 named.hash(state);
262 idx.hash(state);
263 }
264 }
265 }
266}
267
268// Helper function for creating type keys - add aggressive inlining
269#[inline(always)]
270pub fn key_of_type<T: 'static>() -> Key {
271 Key::Type(std::any::TypeId::of::<T>(), std::any::type_name::<T>())
272}