elif_core/container/
tokens.rs

1//! Service token system for type-safe trait injection
2//!
3//! Provides a token-based dependency injection system that enables compile-time
4//! validation of trait implementations and semantic service resolution.
5//!
6//! ## Overview
7//!
8//! The service token system allows developers to define semantic tokens that
9//! represent specific services or traits, enabling dependency inversion through
10//! token-based resolution rather than concrete type dependencies.
11//!
12//! ## Naming Convention
13//!
14//! **Important**: Service tokens should follow the naming convention of ending with "Token"
15//! (e.g., `EmailServiceToken`, `DatabaseToken`). This convention is used by the `#[inject]`
16//! macro to automatically detect token references (`&TokenType`) and differentiate them from
17//! regular reference fields, preventing incorrect macro expansion and compiler errors.
18//!
19//! ## Usage
20//!
21//! ```rust
22//! use elif_core::container::{ServiceToken, IocContainer};
23//! use std::sync::Arc;
24//!
25//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
26//! // Define a service trait
27//! trait EmailService: Send + Sync {
28//!     fn send(&self, to: &str, subject: &str, body: &str) -> Result<(), String>;
29//! }
30//!
31//! // Define a token for the service
32//! struct EmailNotificationToken;
33//! impl ServiceToken for EmailNotificationToken {
34//!     type Service = dyn EmailService;
35//! }
36//!
37//! // Define implementations
38//! #[derive(Default)]
39//! struct SmtpEmailService;
40//! impl EmailService for SmtpEmailService {
41//!     fn send(&self, to: &str, subject: &str, body: &str) -> Result<(), String> {
42//!         println!("SMTP: Sending to {} - {}: {}", to, subject, body);
43//!         Ok(())
44//!     }
45//! }
46//!
47//! // Register with container
48//! let mut container = IocContainer::new();
49//! container.bind_token::<EmailNotificationToken, SmtpEmailService>()?;
50//! container.build()?;
51//!
52//! // Note: Trait object resolution is not yet fully implemented
53//! // This will be available in a future version:
54//! // let service = container.resolve_by_token::<EmailNotificationToken>()?;
55//! // service.send("user@example.com", "Welcome", "Hello!")?;
56//! # Ok(())
57//! # }
58//! ```
59
60use crate::container::descriptor::ServiceId;
61use std::any::TypeId;
62use std::marker::PhantomData;
63
64/// Trait for service tokens that provide compile-time trait-to-implementation mapping
65///
66/// Service tokens are zero-sized types that act as compile-time identifiers for
67/// specific services or traits. They enable type-safe dependency resolution
68/// through semantic naming rather than concrete type dependencies.
69///
70/// ## Design Principles
71///
72/// - **Zero Runtime Cost**: Tokens are zero-sized and only exist at compile time
73/// - **Type Safety**: Prevents incorrect service resolution through type constraints
74/// - **Semantic Naming**: Enables meaningful service identifiers like `EmailNotificationToken`
75/// - **Trait Resolution**: Allows injection of trait objects rather than concrete types
76///
77/// ## Implementation Requirements
78///
79/// - Token must be a zero-sized struct
80/// - Associated `Service` type must be `Send + Sync + 'static`
81/// - Service type is typically a trait object (`dyn Trait`)
82///
83/// ## Examples
84///
85/// ### Basic Trait Token
86/// ```rust
87/// use elif_core::container::ServiceToken;
88/// 
89/// trait Database: Send + Sync {}
90/// struct DatabaseToken;
91/// impl ServiceToken for DatabaseToken {
92///     type Service = dyn Database;
93/// }
94/// ```
95///
96/// ### Specialized Service Token
97/// ```rust
98/// use elif_core::container::ServiceToken;
99/// 
100/// trait CacheService: Send + Sync {
101///     fn get(&self, key: &str) -> Option<String>;
102/// }
103/// 
104/// struct CacheToken;
105/// impl ServiceToken for CacheToken {
106///     type Service = dyn CacheService;
107/// }
108///
109/// struct RedisCache;
110/// impl CacheService for RedisCache {
111///     fn get(&self, _key: &str) -> Option<String> {
112///         None // Mock implementation
113///     }
114/// }
115/// ```
116pub trait ServiceToken: Send + Sync + 'static {
117    /// The service type this token represents
118    ///
119    /// This is typically a trait object (`dyn Trait`) but can be any type
120    /// that implements `Send + Sync + 'static`.
121    type Service: ?Sized + Send + Sync + 'static;
122
123    /// Get the TypeId of the service type
124    ///
125    /// Used internally for service resolution and type checking.
126    /// Default implementation should suffice for most use cases.
127    fn service_type_id() -> TypeId
128    where
129        Self::Service: 'static,
130    {
131        TypeId::of::<Self::Service>()
132    }
133
134    /// Get the type name of the service
135    ///
136    /// Used for debugging and error messages.
137    /// Default implementation should suffice for most use cases.
138    fn service_type_name() -> &'static str {
139        std::any::type_name::<Self::Service>()
140    }
141
142    /// Get the token type name
143    ///
144    /// Used for debugging and error messages.
145    /// Default implementation should suffice for most use cases.
146    fn token_type_name() -> &'static str {
147        std::any::type_name::<Self>()
148    }
149}
150
151/// Metadata for a service token binding
152///
153/// Contains compile-time information about a token-to-implementation binding
154/// for use in dependency resolution and validation.
155#[derive(Debug, Clone)]
156pub struct TokenBinding {
157    /// The token type identifier
158    pub token_type_id: TypeId,
159    /// The token type name (for debugging)
160    pub token_type_name: &'static str,
161    /// The service type identifier
162    pub service_type_id: TypeId,
163    /// The service type name (for debugging)
164    pub service_type_name: &'static str,
165    /// The implementation type identifier
166    pub impl_type_id: TypeId,
167    /// The implementation type name (for debugging)
168    pub impl_type_name: &'static str,
169    /// Optional named identifier for multiple implementations
170    pub name: Option<String>,
171}
172
173impl TokenBinding {
174    /// Create a new token binding
175    pub fn new<Token, Impl>() -> Self
176    where
177        Token: ServiceToken,
178        Impl: Send + Sync + 'static,
179    {
180        Self {
181            token_type_id: TypeId::of::<Token>(),
182            token_type_name: std::any::type_name::<Token>(),
183            service_type_id: Token::service_type_id(),
184            service_type_name: Token::service_type_name(),
185            impl_type_id: TypeId::of::<Impl>(),
186            impl_type_name: std::any::type_name::<Impl>(),
187            name: None,
188        }
189    }
190
191    /// Create a named token binding
192    pub fn named<Token, Impl>(name: impl Into<String>) -> Self
193    where
194        Token: ServiceToken,
195        Impl: Send + Sync + 'static,
196    {
197        let mut binding = Self::new::<Token, Impl>();
198        binding.name = Some(name.into());
199        binding
200    }
201
202    /// Create a ServiceId for this token binding
203    pub fn to_service_id(&self) -> ServiceId {
204        if let Some(name) = &self.name {
205            ServiceId::named_by_ids(self.service_type_id, self.service_type_name, name.clone())
206        } else {
207            ServiceId::by_ids(self.service_type_id, self.service_type_name)
208        }
209    }
210
211    /// Check if this binding matches a token type
212    pub fn matches_token<Token: ServiceToken>(&self) -> bool {
213        self.token_type_id == TypeId::of::<Token>()
214    }
215
216    /// Validate that the implementation can be cast to the service type
217    ///
218    /// This performs compile-time type checking to ensure the implementation
219    /// actually implements the service trait.
220    pub fn validate_implementation<Token, Impl>() -> Result<(), String>
221    where
222        Token: ServiceToken,
223        Impl: Send + Sync + 'static,
224    {
225        let token_service_id = Token::service_type_id();
226        let impl_type_id = TypeId::of::<Impl>();
227
228        // Basic validation - ensure we have the right types
229        if token_service_id == TypeId::of::<()>() {
230            return Err(format!(
231                "Invalid service type for token {}: service type appears to be ()",
232                Token::token_type_name()
233            ));
234        }
235
236        // Check that implementation and service are different types (avoid self-referential bindings)
237        if token_service_id == impl_type_id {
238            return Err(format!(
239                "Token {} maps to itself: implementation type {} cannot be the same as service type {}",
240                Token::token_type_name(),
241                std::any::type_name::<Impl>(),
242                Token::service_type_name()
243            ));
244        }
245
246        // Validate type names for better error messages
247        let token_name = Token::token_type_name();
248        let service_name = Token::service_type_name();
249        let impl_name = std::any::type_name::<Impl>();
250
251        if token_name.is_empty() {
252            return Err("Invalid token: token type name is empty".to_string());
253        }
254
255        if service_name.is_empty() {
256            return Err(format!(
257                "Invalid token {}: service type name is empty",
258                token_name
259            ));
260        }
261
262        if impl_name.is_empty() {
263            return Err(format!(
264                "Invalid implementation for token {}: implementation type name is empty",
265                token_name
266            ));
267        }
268
269        // Additional validation: check for common naming patterns
270        if service_name.contains("dyn ") && impl_name.contains("dyn ") {
271            return Err(format!(
272                "Invalid binding for token {}: both service ({}) and implementation ({}) appear to be trait objects. Implementation should be a concrete type.",
273                token_name,
274                service_name,
275                impl_name
276            ));
277        }
278
279        // Success - validation passed
280        Ok(())
281    }
282}
283
284/// Registry for token-based service bindings
285///
286/// Maintains a mapping from service tokens to their implementations
287/// and provides lookup functionality for the IoC container.
288#[derive(Debug, Default)]
289pub struct TokenRegistry {
290    /// All token bindings indexed by token type
291    bindings: std::collections::HashMap<TypeId, Vec<TokenBinding>>,
292    /// Default bindings (unnamed) indexed by token type  
293    defaults: std::collections::HashMap<TypeId, TokenBinding>,
294    /// Named bindings indexed by token type and name
295    named: std::collections::HashMap<(TypeId, String), TokenBinding>,
296}
297
298impl TokenRegistry {
299    /// Create a new token registry
300    pub fn new() -> Self {
301        Self::default()
302    }
303
304    /// Register a token-to-implementation binding
305    pub fn register<Token, Impl>(&mut self) -> Result<(), String>
306    where
307        Token: ServiceToken,
308        Impl: Send + Sync + 'static,
309    {
310        // Validate the binding
311        TokenBinding::validate_implementation::<Token, Impl>()?;
312
313        let binding = TokenBinding::new::<Token, Impl>();
314        let token_type_id = TypeId::of::<Token>();
315
316        // Add to bindings list
317        self.bindings
318            .entry(token_type_id)
319            .or_default()
320            .push(binding.clone());
321
322        // Set as default if no default exists
323        self.defaults.entry(token_type_id).or_insert(binding);
324
325        Ok(())
326    }
327
328    /// Register a named token-to-implementation binding
329    pub fn register_named<Token, Impl>(&mut self, name: impl Into<String>) -> Result<(), String>
330    where
331        Token: ServiceToken,
332        Impl: Send + Sync + 'static,
333    {
334        // Validate the binding
335        TokenBinding::validate_implementation::<Token, Impl>()?;
336
337        let name = name.into();
338        let binding = TokenBinding::named::<Token, Impl>(&name);
339        let token_type_id = TypeId::of::<Token>();
340
341        // Add to bindings list
342        self.bindings
343            .entry(token_type_id)
344            .or_default()
345            .push(binding.clone());
346
347        // Add to named bindings
348        self.named.insert((token_type_id, name), binding);
349
350        Ok(())
351    }
352
353    /// Get the default binding for a token type
354    pub fn get_default<Token: ServiceToken>(&self) -> Option<&TokenBinding> {
355        let token_type_id = TypeId::of::<Token>();
356        self.defaults.get(&token_type_id)
357    }
358
359    /// Get a named binding for a token type
360    pub fn get_named<Token: ServiceToken>(&self, name: &str) -> Option<&TokenBinding> {
361        let token_type_id = TypeId::of::<Token>();
362        self.named.get(&(token_type_id, name.to_string()))
363    }
364
365    /// Get all bindings for a token type
366    pub fn get_all<Token: ServiceToken>(&self) -> Option<&Vec<TokenBinding>> {
367        let token_type_id = TypeId::of::<Token>();
368        self.bindings.get(&token_type_id)
369    }
370
371    /// Check if a token is registered
372    pub fn contains<Token: ServiceToken>(&self) -> bool {
373        let token_type_id = TypeId::of::<Token>();
374        self.defaults.contains_key(&token_type_id)
375    }
376
377    /// Check if a named token is registered
378    pub fn contains_named<Token: ServiceToken>(&self, name: &str) -> bool {
379        let token_type_id = TypeId::of::<Token>();
380        self.named.contains_key(&(token_type_id, name.to_string()))
381    }
382
383    /// Get all registered token types
384    pub fn token_types(&self) -> Vec<TypeId> {
385        self.defaults.keys().cloned().collect()
386    }
387
388    /// Get statistics about registered tokens
389    pub fn stats(&self) -> TokenRegistryStats {
390        TokenRegistryStats {
391            total_tokens: self.bindings.len(), // Count unique token types from all bindings
392            total_bindings: self.bindings.values().map(|v| v.len()).sum(),
393            named_bindings: self.named.len(),
394        }
395    }
396
397    /// Validate all token bindings in the registry
398    ///
399    /// Returns a list of validation errors found across all registered tokens.
400    /// This method can be used to validate the entire registry after registration.
401    pub fn validate_all_bindings(&self) -> Vec<String> {
402        let mut errors = Vec::new();
403
404        // Check for duplicate bindings
405        for (token_type_id, bindings) in &self.bindings {
406            if bindings.is_empty() {
407                errors.push(format!(
408                    "Token type {:?} has empty bindings list",
409                    token_type_id
410                ));
411                continue;
412            }
413
414            // Check for consistency in token bindings
415            let first_binding = &bindings[0];
416            for binding in bindings.iter().skip(1) {
417                if binding.token_type_id != first_binding.token_type_id {
418                    errors.push(format!(
419                        "Inconsistent token type IDs in bindings for token type {:?}",
420                        token_type_id
421                    ));
422                }
423
424                if binding.service_type_id != first_binding.service_type_id {
425                    errors.push(format!(
426                        "Inconsistent service type IDs for token {}: {} vs {}",
427                        binding.token_type_name,
428                        binding.service_type_name,
429                        first_binding.service_type_name
430                    ));
431                }
432            }
433
434            // Check for named bindings consistency
435            for binding in bindings {
436                if let Some(name) = &binding.name {
437                    if !self
438                        .named
439                        .contains_key(&(binding.token_type_id, name.clone()))
440                    {
441                        errors.push(format!(
442                            "Named binding {} for token {} exists in bindings list but not in named map",
443                            name,
444                            binding.token_type_name
445                        ));
446                    }
447                }
448            }
449        }
450
451        // Check for orphaned named bindings
452        for ((token_type_id, name), binding) in &self.named {
453            if !self.bindings.contains_key(token_type_id) {
454                errors.push(format!(
455                    "Named binding {} for token {} exists in named map but token has no bindings",
456                    name, binding.token_type_name
457                ));
458            }
459        }
460
461        errors
462    }
463
464    /// Check if a token has conflicting bindings
465    pub fn has_binding_conflicts<Token: ServiceToken>(&self) -> Vec<String> {
466        let mut conflicts = Vec::new();
467        let token_type_id = TypeId::of::<Token>();
468
469        if let Some(bindings) = self.bindings.get(&token_type_id) {
470            // Check for multiple default bindings (shouldn't happen but good to check)
471            let default_count = self.defaults.contains_key(&token_type_id) as usize;
472            if default_count == 0 && !bindings.is_empty() {
473                conflicts.push(format!(
474                    "Token {} has bindings but no default binding",
475                    Token::token_type_name()
476                ));
477            }
478
479            // Check for duplicate named bindings (shouldn't happen due to HashMap)
480            let mut seen_names = std::collections::HashSet::new();
481            for binding in bindings {
482                if let Some(name) = &binding.name {
483                    if !seen_names.insert(name.clone()) {
484                        conflicts.push(format!(
485                            "Token {} has duplicate named binding: {}",
486                            Token::token_type_name(),
487                            name
488                        ));
489                    }
490                }
491            }
492        }
493
494        conflicts
495    }
496
497    /// Get detailed information about a token for debugging
498    pub fn get_token_info<Token: ServiceToken>(&self) -> Option<TokenInfo> {
499        let token_type_id = TypeId::of::<Token>();
500        let bindings = self.bindings.get(&token_type_id)?;
501        let default_binding = self.defaults.get(&token_type_id);
502
503        let named_bindings: Vec<String> = self
504            .named
505            .iter()
506            .filter_map(|((tid, name), _)| {
507                if *tid == token_type_id {
508                    Some(name.clone())
509                } else {
510                    None
511                }
512            })
513            .collect();
514
515        Some(TokenInfo {
516            token_name: Token::token_type_name().to_string(),
517            service_name: Token::service_type_name().to_string(),
518            total_bindings: bindings.len(),
519            has_default: default_binding.is_some(),
520            named_bindings,
521            implementation_types: bindings
522                .iter()
523                .map(|b| b.impl_type_name.to_string())
524                .collect(),
525        })
526    }
527
528    /// Clear all bindings (for testing)
529    #[cfg(test)]
530    pub fn clear(&mut self) {
531        self.bindings.clear();
532        self.defaults.clear();
533        self.named.clear();
534    }
535}
536
537/// Statistics about token registry usage
538#[derive(Debug, Clone, PartialEq, Eq)]
539pub struct TokenRegistryStats {
540    /// Total number of distinct token types
541    pub total_tokens: usize,
542    /// Total number of bindings (including named variants)
543    pub total_bindings: usize,
544    /// Number of named bindings
545    pub named_bindings: usize,
546}
547
548/// Detailed information about a specific token for debugging and validation
549#[derive(Debug, Clone)]
550pub struct TokenInfo {
551    /// The token type name
552    pub token_name: String,
553    /// The service type name this token represents
554    pub service_name: String,
555    /// Total number of bindings for this token
556    pub total_bindings: usize,
557    /// Whether this token has a default binding
558    pub has_default: bool,
559    /// List of named binding identifiers
560    pub named_bindings: Vec<String>,
561    /// List of implementation type names
562    pub implementation_types: Vec<String>,
563}
564
565/// Helper trait for working with token references in injection
566///
567/// This trait is implemented for reference types (`&Token`) to enable
568/// seamless resolution of tokens through references in dependency injection.
569pub trait TokenReference {
570    /// The token type this reference points to
571    type Token: ServiceToken;
572
573    /// Get the token type
574    fn token_type() -> PhantomData<Self::Token> {
575        PhantomData
576    }
577}
578
579impl<T: ServiceToken> TokenReference for &T {
580    type Token = T;
581}
582
583#[cfg(test)]
584mod tests {
585    use super::*;
586
587    // Test service trait
588    trait TestService: Send + Sync {
589        #[allow(dead_code)]
590        fn test(&self) -> String;
591    }
592
593    // Test token
594    struct TestToken;
595    impl ServiceToken for TestToken {
596        type Service = dyn TestService;
597    }
598
599    // Test implementation
600    struct TestImpl;
601    impl TestService for TestImpl {
602        fn test(&self) -> String {
603            "test".to_string()
604        }
605    }
606
607    #[test]
608    fn test_service_token_trait() {
609        assert_eq!(
610            TestToken::token_type_name(),
611            "elif_core::container::tokens::tests::TestToken"
612        );
613        assert_eq!(
614            TestToken::service_type_name(),
615            "dyn elif_core::container::tokens::tests::TestService"
616        );
617    }
618
619    #[test]
620    fn test_token_binding_creation() {
621        let binding = TokenBinding::new::<TestToken, TestImpl>();
622
623        assert_eq!(binding.token_type_id, TypeId::of::<TestToken>());
624        assert_eq!(binding.service_type_id, TypeId::of::<dyn TestService>());
625        assert_eq!(binding.impl_type_id, TypeId::of::<TestImpl>());
626        assert!(binding.name.is_none());
627    }
628
629    #[test]
630    fn test_named_token_binding() {
631        let binding = TokenBinding::named::<TestToken, TestImpl>("primary");
632
633        assert_eq!(binding.name, Some("primary".to_string()));
634        assert!(binding.matches_token::<TestToken>());
635    }
636
637    #[test]
638    fn test_token_registry_basic() {
639        let mut registry = TokenRegistry::new();
640
641        assert!(!registry.contains::<TestToken>());
642
643        registry.register::<TestToken, TestImpl>().unwrap();
644
645        assert!(registry.contains::<TestToken>());
646
647        let binding = registry.get_default::<TestToken>().unwrap();
648        assert!(binding.matches_token::<TestToken>());
649    }
650
651    #[test]
652    fn test_token_registry_named() {
653        let mut registry = TokenRegistry::new();
654
655        registry
656            .register_named::<TestToken, TestImpl>("primary")
657            .unwrap();
658
659        assert!(registry.contains_named::<TestToken>("primary"));
660        assert!(!registry.contains_named::<TestToken>("secondary"));
661
662        let binding = registry.get_named::<TestToken>("primary").unwrap();
663        assert_eq!(binding.name, Some("primary".to_string()));
664    }
665
666    #[test]
667    fn test_token_registry_stats() {
668        let mut registry = TokenRegistry::new();
669
670        registry.register::<TestToken, TestImpl>().unwrap();
671        registry
672            .register_named::<TestToken, TestImpl>("primary")
673            .unwrap();
674
675        let stats = registry.stats();
676        assert_eq!(stats.total_tokens, 1);
677        assert_eq!(stats.total_bindings, 2);
678        assert_eq!(stats.named_bindings, 1);
679    }
680
681    #[test]
682    fn test_token_reference() {
683        let _phantom: PhantomData<TestToken> = <&TestToken as TokenReference>::token_type();
684        // This test mainly ensures the TokenReference trait compiles correctly
685    }
686}