use crate::{
DiResult, FactoryOutput, InjectableKey, context::InjectionContext, injected::DependencyScope,
injected::InjectionMetadata,
};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
#[derive(Debug)]
pub struct Depends<K, T>
where
K: InjectableKey,
T: Send + Sync + 'static,
{
inner: Arc<FactoryOutput<K, T>>,
metadata: InjectionMetadata,
}
impl<K, T> Depends<K, T>
where
K: InjectableKey,
T: Send + Sync + 'static,
{
pub fn builder() -> DependsBuilder<K, T> {
DependsBuilder {
use_cache: true,
_phantom: PhantomData,
}
}
pub fn builder_no_cache() -> DependsBuilder<K, T> {
DependsBuilder {
use_cache: false,
_phantom: PhantomData,
}
}
pub async fn resolve_from_registry(ctx: &InjectionContext, use_cache: bool) -> DiResult<Self> {
let output = ctx
.resolve_with_cache::<FactoryOutput<K, T>>(use_cache)
.await?;
Ok(Self::from_output(output, use_cache))
}
pub fn from_value(value: T) -> Self {
Self::from_output(Arc::new(FactoryOutput::new(value)), false)
}
pub fn from_output(output: Arc<FactoryOutput<K, T>>, use_cache: bool) -> Self {
Self {
inner: output,
metadata: InjectionMetadata {
scope: DependencyScope::Request,
cached: use_cache,
},
}
}
pub fn as_output(&self) -> &FactoryOutput<K, T> {
self.inner.as_ref()
}
pub fn as_arc(&self) -> &Arc<FactoryOutput<K, T>> {
&self.inner
}
pub fn metadata(&self) -> &InjectionMetadata {
&self.metadata
}
pub fn try_unwrap(self) -> Result<T, Self> {
match Arc::try_unwrap(self.inner) {
Ok(output) => Ok(output.into_inner()),
Err(output) => Err(Self {
inner: output,
metadata: self.metadata,
}),
}
}
}
impl<K, T> Depends<K, T>
where
K: InjectableKey,
T: Clone + Send + Sync + 'static,
{
pub fn into_inner(self) -> T {
Arc::try_unwrap(self.inner)
.map(FactoryOutput::into_inner)
.unwrap_or_else(|output| output.as_ref().as_ref().clone())
}
}
pub struct DependsBuilder<K, T>
where
K: InjectableKey,
T: Send + Sync + 'static,
{
use_cache: bool,
_phantom: PhantomData<fn() -> (K, T)>,
}
impl<K, T> DependsBuilder<K, T>
where
K: InjectableKey,
T: Send + Sync + 'static,
{
pub async fn resolve(self, ctx: &InjectionContext) -> DiResult<Depends<K, T>> {
Depends::<K, T>::resolve_from_registry(ctx, self.use_cache).await
}
}
impl<K, T> Deref for Depends<K, T>
where
K: InjectableKey,
T: Send + Sync + 'static,
{
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().as_ref()
}
}
impl<K, T> Clone for Depends<K, T>
where
K: InjectableKey,
T: Send + Sync + 'static,
{
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
metadata: self.metadata,
}
}
}
impl<K, T> AsRef<T> for Depends<K, T>
where
K: InjectableKey,
T: Send + Sync + 'static,
{
fn as_ref(&self) -> &T {
self.inner.as_ref().as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct TestKey;
impl InjectableKey for TestKey {}
#[derive(Clone, Debug, PartialEq, Eq)]
struct TestConfig {
value: String,
}
#[test]
fn from_value_wraps_factory_output() {
let depends = Depends::<TestKey, TestConfig>::from_value(TestConfig {
value: "custom".to_string(),
});
assert_eq!(depends.value, "custom");
assert_eq!(
depends.as_output().as_ref(),
&TestConfig {
value: "custom".to_string(),
}
);
assert!(!depends.metadata().cached);
}
#[test]
fn from_output_preserves_cache_metadata() {
let output = Arc::new(FactoryOutput::<TestKey, TestConfig>::new(TestConfig {
value: "shared".to_string(),
}));
let depends = Depends::from_output(Arc::clone(&output), true);
assert_eq!(depends.value, "shared");
assert!(depends.metadata().cached);
assert_eq!(depends.metadata().scope, DependencyScope::Request);
assert!(Arc::ptr_eq(depends.as_arc(), &output));
}
#[test]
fn try_unwrap_returns_value_for_single_owner() {
let depends = Depends::<TestKey, TestConfig>::from_value(TestConfig {
value: "owned".to_string(),
});
assert_eq!(
depends.try_unwrap().unwrap(),
TestConfig {
value: "owned".to_string(),
}
);
}
#[test]
fn try_unwrap_returns_self_for_shared_output() {
let depends = Depends::<TestKey, TestConfig>::from_value(TestConfig {
value: "shared".to_string(),
});
let _clone = depends.clone();
let returned = depends.try_unwrap().unwrap_err();
assert_eq!(returned.value, "shared");
assert_eq!(returned.metadata().scope, DependencyScope::Request);
}
#[test]
fn into_inner_clones_shared_output() {
let depends = Depends::<TestKey, TestConfig>::from_value(TestConfig {
value: "cloned".to_string(),
});
let cloned = depends.clone();
assert_eq!(
depends.into_inner(),
TestConfig {
value: "cloned".to_string(),
}
);
assert_eq!(cloned.value, "cloned");
}
}