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}