ServiceProvider

Struct ServiceProvider 

Source
pub struct ServiceProvider { /* private fields */ }
Expand description

Service provider for resolving dependencies from the DI container.

The ServiceProvider is the heart of the dependency injection system. It resolves services according to their registered lifetimes (Singleton, Scoped, Transient) and manages the lifecycle of singleton services including disposal.

§Performance Optimizations

ServiceProvider includes world-class performance optimizations:

  • Singleton caching: Embedded OnceCell provides 31ns resolution (~31.5M ops/sec)
  • Scoped caching: Slot-based resolution with O(1) access times
  • Hybrid registry: Vec for small collections, HashMap for large ones
  • Lock-free reads: After initialization, singleton access requires no locks

§Thread Safety

ServiceProvider is fully thread-safe and can be shared across multiple threads. Singleton services are cached with proper synchronization, and the provider can be cloned cheaply (it uses Arc internally).

§Examples

use ferrous_di::{ServiceCollection, Resolver};
use std::sync::Arc;

struct Database { url: String }
struct UserService { db: Arc<Database> }

let mut collection = ServiceCollection::new();
collection.add_singleton(Database { url: "postgres://localhost".to_string() });
collection.add_transient_factory::<UserService, _>(|resolver| {
    UserService { db: resolver.get_required::<Database>() }
});

let provider = collection.build();
let user_service = provider.get_required::<UserService>();
assert_eq!(user_service.db.url, "postgres://localhost");

Implementations§

Source§

impl ServiceProvider

Source

pub fn create_scope(&self) -> Scope

Creates a new scope for resolving scoped services.

Scoped services are cached per scope and are ideal for request-scoped dependencies in web applications. Each scope maintains its own cache of scoped services while still accessing singleton services from the root provider.

§Returns

A new Scope that can resolve both scoped and singleton services. The scope maintains its own cache for scoped services.

§Examples
use ferrous_di::{ServiceCollection, Resolver};
use std::sync::{Arc, Mutex};

#[derive(Debug)]
struct RequestId(String);

let mut collection = ServiceCollection::new();
let counter = Arc::new(Mutex::new(0));
let counter_clone = counter.clone();

collection.add_scoped_factory::<RequestId, _>(move |_| {
    let mut c = counter_clone.lock().unwrap();
    *c += 1;
    RequestId(format!("req-{}", *c))
});

let provider = collection.build();

// Create separate scopes
let scope1 = provider.create_scope();
let scope2 = provider.create_scope();

let req1a = scope1.get_required::<RequestId>();
let req1b = scope1.get_required::<RequestId>(); // Same instance
let req2 = scope2.get_required::<RequestId>(); // Different instance

assert!(Arc::ptr_eq(&req1a, &req1b)); // Same scope, same instance
assert!(!Arc::ptr_eq(&req1a, &req2)); // Different scopes, different instances
Source

pub async fn dispose_all(&self)

Disposes all registered disposal hooks in LIFO order.

This method runs all asynchronous disposal hooks first (in reverse order), followed by all synchronous disposal hooks (in reverse order). This ensures proper cleanup of singleton services.

§Examples
use ferrous_di::{ServiceCollection, Dispose, AsyncDispose, Resolver};
use async_trait::async_trait;
use std::sync::Arc;

struct Cache;
impl Dispose for Cache {
    fn dispose(&self) {
        println!("Cache disposed");
    }
}

struct Client;
#[async_trait]
impl AsyncDispose for Client {
    async fn dispose(&self) {
        println!("Client disposed");
    }
}

let mut services = ServiceCollection::new();
services.add_singleton_factory::<Cache, _>(|r| {
    let cache = Arc::new(Cache);
    r.register_disposer(cache.clone());
    Cache // Return concrete type
});
services.add_singleton_factory::<Client, _>(|r| {
    let client = Arc::new(Client);
    r.register_async_disposer(client.clone());
    Client // Return concrete type
});

let provider = services.build();
// ... use services ...
provider.dispose_all().await;
Source§

impl ServiceProvider

Source

pub fn resolve_singleton_fast_cache( &self, key: &Key, ) -> Option<Arc<dyn Any + Send + Sync>>

Alternative high-performance singleton resolution using FastSingletonCache This provides an alternative to the embedded OnceCell approach for scenarios where maximum throughput is needed and error handling can be simplified

Source

pub fn discover_tools( &self, criteria: &ToolSelectionCriteria, ) -> ToolDiscoveryResult

Discovers available tools based on capability requirements.

This is the main entry point for agent planners to find suitable tools for their tasks. Returns matching tools along with partial matches and any unsatisfied requirements.

§Examples
use ferrous_di::{ServiceCollection, ToolSelectionCriteria, CapabilityRequirement};

// ... after registering tools with capabilities ...
let mut services = ServiceCollection::new();
let provider = services.build();

// Find tools that can search the web
let criteria = ToolSelectionCriteria::new()
    .require("web_search")
    .exclude_tag("experimental")
    .max_cost(0.01);

let result = provider.discover_tools(&criteria);

println!("Found {} matching tools", result.matching_tools.len());
for tool in &result.matching_tools {
    println!("  - {}: {}", tool.name, tool.description);
}

if !result.unsatisfied_requirements.is_empty() {
    println!("Missing capabilities: {:?}", result.unsatisfied_requirements);
}
Source

pub fn list_all_tools(&self) -> Vec<&ToolInfo>

Gets all registered tools with their capability information.

Useful for debugging or building tool catalogs.

Source

pub fn get_tool_info(&self, key: &Key) -> Option<&ToolInfo>

Gets capability information for a specific tool.

Source§

impl ServiceProvider

Source

pub async fn ready( &self, ) -> Result<ReadinessReport, Box<dyn Error + Send + Sync>>

Performs readiness checks on all prewarmed services.

This method resolves all services marked for prewarming and runs readiness checks on those that implement ReadyCheck. This is typically called during application startup to ensure all critical services are ready before accepting requests.

§Performance

Readiness checks are run in parallel with a configurable concurrency limit to balance startup time with resource usage.

§Examples
use ferrous_di::{ServiceCollection, ReadyCheck};
use async_trait::async_trait;
use std::sync::Arc;

struct DatabaseService;
#[async_trait]
impl ReadyCheck for DatabaseService {
    async fn ready(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // Test database connection
        Ok(())
    }
}

let mut services = ServiceCollection::new();
services.add_singleton(DatabaseService);
services.prewarm::<DatabaseService>();

let provider = services.build();
let report = provider.ready().await?;

if report.all_ready() {
    println!("All services ready! Starting application...");
} else {
    eprintln!("Some services failed readiness checks:");
    for failure in report.failures() {
        eprintln!("  {}: {}", 
            failure.key.display_name(), 
            failure.error.as_deref().unwrap_or("Unknown error"));
    }
    std::process::exit(1);
}

Trait Implementations§

Source§

impl Clone for ServiceProvider

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Drop for ServiceProvider

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl LabeledScopeExt for ServiceProvider

Source§

fn create_labeled_scope(&self, label: &'static str) -> LabeledScope

Creates a new labeled scope. Read more
Source§

impl Resolver for ServiceProvider

Source§

fn register_disposer<T>(&self, service: Arc<T>)
where T: Dispose + 'static,

Registers a service for synchronous disposal. Read more
Source§

fn register_async_disposer<T>(&self, service: Arc<T>)
where T: AsyncDispose + 'static,

Registers a service for asynchronous disposal. Read more
Source§

fn get<T: 'static + Send + Sync>(&self) -> DiResult<Arc<T>>

Resolves a concrete service type. Read more
Source§

fn get_trait<T: ?Sized + 'static + Send + Sync>(&self) -> DiResult<Arc<T>>
where Arc<T>: 'static,

Resolves a single trait implementation. Read more
Source§

fn get_all_trait<T: ?Sized + 'static + Send + Sync>( &self, ) -> DiResult<Vec<Arc<T>>>
where Arc<T>: 'static,

Resolves all registered implementations of a trait. Read more
Source§

fn get_required<T: 'static + Send + Sync>(&self) -> Arc<T>

Resolves a concrete service type, panicking on failure. Read more
Source§

fn get_required_trait<T: ?Sized + 'static + Send + Sync>(&self) -> Arc<T>
where Arc<T>: 'static,

Resolves a trait implementation, panicking on failure. Read more
Source§

fn get_named<T: 'static + Send + Sync>( &self, name: &'static str, ) -> DiResult<Arc<T>>

Resolves a named concrete service type. Read more
Source§

fn get_named_required<T: 'static + Send + Sync>( &self, name: &'static str, ) -> Arc<T>

Resolves a named concrete service type, panicking on failure.
Source§

fn get_named_trait<T: ?Sized + 'static + Send + Sync>( &self, name: &'static str, ) -> DiResult<Arc<T>>
where Arc<T>: 'static,

Resolves a named trait implementation.
Source§

fn get_named_trait_required<T: ?Sized + 'static + Send + Sync>( &self, name: &'static str, ) -> Arc<T>
where Arc<T>: 'static,

Resolves a named trait implementation, panicking on failure.
Source§

impl ResolverCore for ServiceProvider

Source§

fn resolve_any(&self, key: &Key) -> DiResult<Arc<dyn Any + Send + Sync>>

Resolves a single service using thread-local stack for circular dependency detection. Read more
Source§

fn resolve_many(&self, key: &Key) -> DiResult<Vec<Arc<dyn Any + Send + Sync>>>

Resolves all multi-bound services for a trait using circular dependency detection. Read more
Source§

fn push_sync_disposer(&self, f: Box<dyn FnOnce() + Send>)

Registers a synchronous disposal hook. Read more
Source§

fn push_async_disposer( &self, f: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send>, )

Registers an asynchronous disposal hook. Read more
Source§

fn resolve_any_internal( &self, key: &Key, ) -> DiResult<Arc<dyn Any + Send + Sync>>

Legacy internal resolve method for compatibility. Read more
Source§

fn resolve_many_internal( &self, key: &Key, ) -> DiResult<Vec<Arc<dyn Any + Send + Sync>>>

Legacy internal resolve many method for compatibility. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.