use std::sync::Arc;
use crate::{Extract, InjectableResult, ResolveContext};
pub struct FactoryCtx(pub(crate) Arc<ResolveContext>);
impl FactoryCtx {
pub(crate) fn new(ctx: Arc<ResolveContext>) -> Self {
Self(ctx)
}
pub async fn extract<T>(&self) -> InjectableResult<T>
where
T: Extract + Send + Sync + 'static,
{
T::extract(&self.0).await
}
pub async fn resolve_external<T>(&self) -> InjectableResult<T>
where
T: Send + Sync + 'static,
{
self.0.resolve_external::<T>().await
}
pub async fn resolve_external_with_token<T>(&self, token: &str) -> InjectableResult<T>
where
T: Send + Sync + 'static,
{
self.0.resolve_external_with_token::<T>(token).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
DynProvider, EmptySingletonStore, Injectable, InjectableError, InjectableResult, Provider,
ProviderRegistry,
};
use std::sync::Arc;
#[derive(Debug, Default, Clone)]
struct Leaf;
struct LeafProvider;
#[async_trait::async_trait]
impl Provider<Leaf> for LeafProvider {
async fn provide(_ctx: &ResolveContext) -> InjectableResult<Leaf> {
Ok(Leaf)
}
}
impl Injectable for Leaf {
type Provider = LeafProvider;
const IS_SINGLETON: bool = true;
}
fn make_ctx() -> Arc<ResolveContext> {
Arc::new(ResolveContext::new(
Arc::new(EmptySingletonStore),
Arc::new(ProviderRegistry::new()),
))
}
#[tokio::test]
async fn extract_arc_t_returns_singleton() {
let ctx = make_ctx();
let fctx = FactoryCtx::new(Arc::clone(&ctx));
let a: Arc<Leaf> = fctx.extract().await.expect("first extraction");
let b: Arc<Leaf> = fctx.extract().await.expect("second extraction");
assert!(
Arc::ptr_eq(&a, &b),
"FactoryCtx::extract::<Arc<T>> should return the cached singleton Arc"
);
}
#[tokio::test]
async fn two_factory_ctx_share_singleton() {
let ctx = make_ctx();
let fctx1 = FactoryCtx::new(Arc::clone(&ctx));
let fctx2 = FactoryCtx::new(Arc::clone(&ctx));
let a: Arc<Leaf> = fctx1.extract().await.expect("first ctx");
let b: Arc<Leaf> = fctx2.extract().await.expect("second ctx");
assert!(
Arc::ptr_eq(&a, &b),
"both FactoryCtx instances must return the same singleton (same underlying context)"
);
}
#[tokio::test]
async fn resolve_external_returns_registered_value() {
let mut registry = ProviderRegistry::new();
registry.register("", DynProvider::from_value(42u32));
let ctx = Arc::new(ResolveContext::new(
Arc::new(EmptySingletonStore),
Arc::new(registry),
));
let fctx = FactoryCtx::new(ctx);
let val: u32 = fctx.resolve_external().await.expect("registered u32");
assert_eq!(val, 42);
}
#[tokio::test]
async fn resolve_external_missing_returns_error() {
let fctx = FactoryCtx::new(make_ctx());
let result: InjectableResult<String> = fctx.resolve_external().await;
assert!(
matches!(result, Err(InjectableError::MissingDependency { .. })),
"unregistered type should yield MissingDependency, got: {:?}",
result.unwrap_err()
);
}
}