ferro_rs/container/provider.rs
1//! Service auto-registration for Ferro framework
2//!
3//! This module provides automatic service registration via macros:
4//! - `#[service(ConcreteType)]` - auto-register trait bindings
5//! - `#[derive(Injectable)]` - auto-register concrete types as singletons
6//!
7//! # Example - Trait binding
8//!
9//! ```rust,ignore
10//! use ferro_rs::service;
11//!
12//! // Auto-register: dyn CacheStore → RedisCache
13//! #[service(RedisCache)]
14//! pub trait CacheStore: Send + Sync + 'static {
15//! fn get(&self, key: &str) -> Option<String>;
16//! fn set(&self, key: &str, value: &str);
17//! }
18//!
19//! pub struct RedisCache;
20//! impl Default for RedisCache {
21//! fn default() -> Self { Self }
22//! }
23//! impl CacheStore for RedisCache { ... }
24//! ```
25//!
26//! # Example - Concrete singleton
27//!
28//! ```rust,ignore
29//! use ferro_rs::injectable;
30//!
31//! #[injectable]
32//! pub struct AppState {
33//! pub counter: u32,
34//! }
35//!
36//! // Resolve via:
37//! let state: AppState = App::get().unwrap();
38//! ```
39
40/// Entry for inventory-collected service bindings (trait → impl)
41///
42/// Used internally by the `#[service(ConcreteType)]` macro to register
43/// service bindings at compile time.
44pub struct ServiceBindingEntry {
45 /// Function to register the service binding
46 pub register: fn(),
47 /// Service name for debugging/logging
48 pub name: &'static str,
49}
50
51/// Entry for inventory-collected singleton registrations (concrete types)
52///
53/// Used internally by the `#[derive(Injectable)]` macro to register
54/// concrete singletons at compile time.
55pub struct SingletonEntry {
56 /// Function to register the singleton
57 pub register: fn(),
58 /// Type name for debugging/logging
59 pub name: &'static str,
60}
61
62// Inventory collection for auto-registered service bindings
63inventory::collect!(ServiceBindingEntry);
64
65// Inventory collection for auto-registered singletons
66inventory::collect!(SingletonEntry);
67
68/// Register all service bindings from inventory
69///
70/// This is called automatically by `Server::from_config()`.
71/// It registers all services marked with `#[service(ConcreteType)]`.
72pub fn register_service_bindings() {
73 for entry in inventory::iter::<ServiceBindingEntry> {
74 (entry.register)();
75 }
76}
77
78/// Register all singleton entries from inventory
79///
80/// This is called automatically by `Server::from_config()`.
81/// It registers all types marked with `#[derive(Injectable)]`.
82pub fn register_singletons() {
83 for entry in inventory::iter::<SingletonEntry> {
84 (entry.register)();
85 }
86}
87
88/// Full bootstrap sequence for services
89///
90/// Called automatically by `Server::from_config()`.
91pub fn bootstrap() {
92 register_service_bindings();
93 register_singletons();
94}
95
96/// Service info for introspection
97#[derive(Debug, Clone, serde::Serialize)]
98pub struct ServiceInfo {
99 /// Service name (trait or concrete type)
100 pub name: String,
101 /// Type of binding
102 pub binding_type: ServiceBindingType,
103}
104
105/// Type of service binding
106#[derive(Debug, Clone, serde::Serialize)]
107#[serde(rename_all = "snake_case")]
108pub enum ServiceBindingType {
109 /// Trait binding (dyn Trait → Impl)
110 TraitBinding,
111 /// Concrete singleton
112 Singleton,
113}
114
115/// Get all registered services for introspection
116///
117/// Returns a list of all services registered via `#[service(ConcreteType)]`
118/// and `#[injectable]` macros.
119pub fn get_registered_services() -> Vec<ServiceInfo> {
120 let mut services = Vec::new();
121
122 // Collect trait bindings
123 for entry in inventory::iter::<ServiceBindingEntry> {
124 services.push(ServiceInfo {
125 name: entry.name.to_string(),
126 binding_type: ServiceBindingType::TraitBinding,
127 });
128 }
129
130 // Collect singletons
131 for entry in inventory::iter::<SingletonEntry> {
132 services.push(ServiceInfo {
133 name: entry.name.to_string(),
134 binding_type: ServiceBindingType::Singleton,
135 });
136 }
137
138 services
139}