mirror of
https://github.com/TECHNOFAB11/aerosol.git
synced 2025-12-10 23:20:06 +01:00
Implementations for Arc and other wrapping types.
This commit is contained in:
parent
f8ebe14790
commit
e5f497756a
8 changed files with 197 additions and 26 deletions
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.cargo.features": "all"
|
||||
}
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
[package]
|
||||
name = "aerosol"
|
||||
version = "1.0.0-alpha.1"
|
||||
version = "1.0.0-alpha.2"
|
||||
authors = ["Diggory Blake <diggsey@googlemail.com>"]
|
||||
edition = "2018"
|
||||
description = "Simple dependency injection for Rust"
|
||||
repository = "https://github.com/Diggsey/aerosol"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
async = ["async-trait"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::error::Error;
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
|
|
@ -6,13 +6,13 @@ use crate::{
|
|||
resource::{unwrap_constructed, Resource},
|
||||
slot::SlotDesc,
|
||||
state::Aerosol,
|
||||
ConstructibleResource,
|
||||
sync_constructible::Constructible,
|
||||
};
|
||||
|
||||
/// Implemented for resources which can be constructed asynchronously from other
|
||||
/// Implemented for values which can be constructed asynchronously from other
|
||||
/// resources. Requires feature `async`.
|
||||
#[async_trait]
|
||||
pub trait AsyncConstructibleResource: Resource {
|
||||
pub trait AsyncConstructible: Sized {
|
||||
/// Error type for when resource fails to be constructed.
|
||||
type Error: Error + Send + Sync;
|
||||
/// Construct the resource with the provided application state.
|
||||
|
|
@ -20,13 +20,59 @@ pub trait AsyncConstructibleResource: Resource {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: ConstructibleResource> AsyncConstructibleResource for T {
|
||||
type Error = <T as ConstructibleResource>::Error;
|
||||
impl<T: Constructible> AsyncConstructible for T {
|
||||
type Error = <T as Constructible>::Error;
|
||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
Self::construct(aero)
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically implemented for values which can be indirectly asynchronously constructed from other resources.
|
||||
/// Requires feature `async`.
|
||||
#[async_trait]
|
||||
pub trait IndirectlyAsyncConstructible: Sized {
|
||||
/// Error type for when resource fails to be constructed.
|
||||
type Error: Error + Send + Sync;
|
||||
/// Construct the resource with the provided application state.
|
||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: AsyncConstructible> IndirectlyAsyncConstructible for T {
|
||||
type Error = T::Error;
|
||||
|
||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
T::construct_async(aero).await
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_async_constructible {
|
||||
(<$t:ident>; $($x:ty: $y:expr;)*) => {
|
||||
$(
|
||||
#[async_trait]
|
||||
impl<$t: IndirectlyAsyncConstructible> IndirectlyAsyncConstructible for $x {
|
||||
type Error = $t::Error;
|
||||
|
||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
$t::construct_async(aero).await.map($y)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
impl_async_constructible! {
|
||||
<T>;
|
||||
Arc<T>: Arc::new;
|
||||
std::sync::Mutex<T>: std::sync::Mutex::new;
|
||||
parking_lot::Mutex<T>: parking_lot::Mutex::new;
|
||||
std::sync::RwLock<T>: std::sync::RwLock::new;
|
||||
parking_lot::RwLock<T>: parking_lot::RwLock::new;
|
||||
}
|
||||
|
||||
/// Implemented for resources which can be asynchronously constructed from other resources. Requires feature `async`.
|
||||
pub trait AsyncConstructibleResource: Resource + IndirectlyAsyncConstructible {}
|
||||
impl<T: Resource + IndirectlyAsyncConstructible> AsyncConstructibleResource for T {}
|
||||
|
||||
impl Aerosol {
|
||||
/// Try to get or construct an instance of `T` asynchronously. Requires feature `async`.
|
||||
pub async fn try_obtain_async<T: AsyncConstructibleResource>(&self) -> Result<T, T::Error> {
|
||||
|
|
@ -49,7 +95,27 @@ impl Aerosol {
|
|||
}
|
||||
/// Get or construct an instance of `T` asynchronously. Panics if unable. Requires feature `async`.
|
||||
pub async fn obtain_async<T: AsyncConstructibleResource>(&self) -> T {
|
||||
unwrap_constructed(self.try_obtain_async::<T>().await)
|
||||
unwrap_constructed::<T, _>(self.try_obtain_async::<T>().await)
|
||||
}
|
||||
/// Try to initialize an instance of `T` asynchronously. Does nothing if `T` is already initialized.
|
||||
pub async fn try_init_async<T: AsyncConstructibleResource>(&self) -> Result<(), T::Error> {
|
||||
match self.wait_for_slot_async::<T>(true).await {
|
||||
Some(_) => Ok(()),
|
||||
None => match T::construct_async(self).await {
|
||||
Ok(x) => {
|
||||
self.fill_placeholder::<T>(x);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
self.clear_placeholder::<T>();
|
||||
Err(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Initialize an instance of `T` asynchronously. Does nothing if `T` is already initialized. Panics if unable.
|
||||
pub async fn init_async<T: AsyncConstructibleResource>(&self) {
|
||||
unwrap_constructed::<T, _>(self.try_init_async::<T>().await)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +129,7 @@ mod tests {
|
|||
struct Dummy;
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncConstructibleResource for Dummy {
|
||||
impl AsyncConstructible for Dummy {
|
||||
type Error = Infallible;
|
||||
|
||||
async fn construct_async(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -97,7 +163,7 @@ mod tests {
|
|||
struct DummyRecursive;
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncConstructibleResource for DummyRecursive {
|
||||
impl AsyncConstructible for DummyRecursive {
|
||||
type Error = Infallible;
|
||||
|
||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -128,7 +194,7 @@ mod tests {
|
|||
struct DummyCyclic;
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncConstructibleResource for DummyCyclic {
|
||||
impl AsyncConstructible for DummyCyclic {
|
||||
type Error = Infallible;
|
||||
|
||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -147,7 +213,7 @@ mod tests {
|
|||
#[derive(Debug, Clone)]
|
||||
struct DummySync;
|
||||
|
||||
impl ConstructibleResource for DummySync {
|
||||
impl Constructible for DummySync {
|
||||
type Error = Infallible;
|
||||
|
||||
fn construct(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -160,7 +226,7 @@ mod tests {
|
|||
struct DummySyncRecursive;
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncConstructibleResource for DummySyncRecursive {
|
||||
impl AsyncConstructible for DummySyncRecursive {
|
||||
type Error = Infallible;
|
||||
|
||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -186,4 +252,23 @@ mod tests {
|
|||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DummyNonClone;
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncConstructible for DummyNonClone {
|
||||
type Error = Infallible;
|
||||
|
||||
async fn construct_async(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn obtain_non_clone() {
|
||||
let state = Aerosol::new();
|
||||
state.obtain_async::<Arc<DummyNonClone>>().await;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Integration with the `axum` web framework.
|
||||
//!
|
||||
//! Provies the `Dep` and `Obtain` axum extractors for easily accessing
|
||||
//! Provides the `Dep` and `Obtain` axum extractors for easily accessing
|
||||
//! resources from within route handlers.
|
||||
//!
|
||||
//! To make use of these extractors, your application state must either be
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@
|
|||
//! # aerosol
|
||||
//! Simple dependency injection for Rust
|
||||
//!
|
||||
//! Optional features: `async`
|
||||
//! Optional features:
|
||||
//!
|
||||
//! ## `async`
|
||||
//!
|
||||
//! Allows resources to be constructed asynchrously, and provies a corresponding
|
||||
//! Allows resources to be constructed asynchrously, and provides a corresponding
|
||||
//! `AsyncConstructibleResource` trait.
|
||||
//!
|
||||
//! ## `axum`
|
||||
//!
|
||||
//! Provies integrations with the `axum` web framework. See the `axum` module
|
||||
//! Provides integrations with the `axum` web framework. See the `axum` module
|
||||
//! for more information.
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub(crate) fn unwrap_resource<T: Resource>(opt: Option<T>) -> T {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unwrap_constructed<T: Resource, E: Error>(res: Result<T, E>) -> T {
|
||||
pub(crate) fn unwrap_constructed<T: Resource, U>(res: Result<U, impl Error>) -> U {
|
||||
match res {
|
||||
Ok(x) => x,
|
||||
Err(e) => panic!("Failed to construct `{}`: {}", type_name::<T>(), e),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct InnerAerosol {
|
|||
}
|
||||
|
||||
/// Stores a collection of resources keyed on resource type.
|
||||
/// Provies methods for accessing this collection.
|
||||
/// Provides methods for accessing this collection.
|
||||
/// Can be cheaply cloned.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Aerosol {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::error::Error;
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
resource::{unwrap_constructed, Resource},
|
||||
|
|
@ -6,14 +6,56 @@ use crate::{
|
|||
state::Aerosol,
|
||||
};
|
||||
|
||||
/// Implemented for resources which can be constructed from other resources.
|
||||
pub trait ConstructibleResource: Resource {
|
||||
/// Implemented for values which can be constructed from other resources.
|
||||
pub trait Constructible: Sized {
|
||||
/// Error type for when resource fails to be constructed.
|
||||
type Error: Error + Send + Sync;
|
||||
/// Construct the resource with the provided application state.
|
||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
/// Automatically implemented for values which can be indirectly constructed from other resources.
|
||||
pub trait IndirectlyConstructible: Sized {
|
||||
/// Error type for when resource fails to be constructed.
|
||||
type Error: Error + Send + Sync;
|
||||
/// Construct the resource with the provided application state.
|
||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T: Constructible> IndirectlyConstructible for T {
|
||||
type Error = T::Error;
|
||||
|
||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
T::construct(aero)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_constructible {
|
||||
(<$t:ident>; $($x:ty: $y:expr;)*) => {
|
||||
$(
|
||||
impl<$t: IndirectlyConstructible> IndirectlyConstructible for $x {
|
||||
type Error = $t::Error;
|
||||
|
||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
$t::construct(aero).map($y)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
impl_constructible! {
|
||||
<T>;
|
||||
Arc<T>: Arc::new;
|
||||
std::sync::Mutex<T>: std::sync::Mutex::new;
|
||||
parking_lot::Mutex<T>: parking_lot::Mutex::new;
|
||||
std::sync::RwLock<T>: std::sync::RwLock::new;
|
||||
parking_lot::RwLock<T>: parking_lot::RwLock::new;
|
||||
}
|
||||
|
||||
/// Implemented for resources which can be constructed from other resources.
|
||||
pub trait ConstructibleResource: Resource + IndirectlyConstructible {}
|
||||
impl<T: Resource + IndirectlyConstructible> ConstructibleResource for T {}
|
||||
|
||||
impl Aerosol {
|
||||
/// Try to get or construct an instance of `T`.
|
||||
pub fn try_obtain<T: ConstructibleResource>(&self) -> Result<T, T::Error> {
|
||||
|
|
@ -36,7 +78,27 @@ impl Aerosol {
|
|||
}
|
||||
/// Get or construct an instance of `T`. Panics if unable.
|
||||
pub fn obtain<T: ConstructibleResource>(&self) -> T {
|
||||
unwrap_constructed(self.try_obtain::<T>())
|
||||
unwrap_constructed::<T, _>(self.try_obtain::<T>())
|
||||
}
|
||||
/// Try to initialize an instance of `T`. Does nothing if `T` is already initialized.
|
||||
pub fn try_init<T: ConstructibleResource>(&self) -> Result<(), T::Error> {
|
||||
match self.wait_for_slot::<T>(true) {
|
||||
Some(_) => Ok(()),
|
||||
None => match T::construct(self) {
|
||||
Ok(x) => {
|
||||
self.fill_placeholder::<T>(x);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
self.clear_placeholder::<T>();
|
||||
Err(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Initialize an instance of `T`. Does nothing if `T` is already initialized. Panics if unable.
|
||||
pub fn init<T: ConstructibleResource>(&self) {
|
||||
unwrap_constructed::<T, _>(self.try_init::<T>())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +111,7 @@ mod tests {
|
|||
#[derive(Debug, Clone)]
|
||||
struct Dummy;
|
||||
|
||||
impl ConstructibleResource for Dummy {
|
||||
impl Constructible for Dummy {
|
||||
type Error = Infallible;
|
||||
|
||||
fn construct(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -77,7 +139,7 @@ mod tests {
|
|||
#[derive(Debug, Clone)]
|
||||
struct DummyRecursive;
|
||||
|
||||
impl ConstructibleResource for DummyRecursive {
|
||||
impl Constructible for DummyRecursive {
|
||||
type Error = Infallible;
|
||||
|
||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -105,7 +167,7 @@ mod tests {
|
|||
#[derive(Debug, Clone)]
|
||||
struct DummyCyclic;
|
||||
|
||||
impl ConstructibleResource for DummyCyclic {
|
||||
impl Constructible for DummyCyclic {
|
||||
type Error = Infallible;
|
||||
|
||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
||||
|
|
@ -120,4 +182,22 @@ mod tests {
|
|||
let state = Aerosol::new();
|
||||
state.obtain::<DummyCyclic>();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DummyNonClone;
|
||||
|
||||
impl Constructible for DummyNonClone {
|
||||
type Error = Infallible;
|
||||
|
||||
fn construct(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obtain_non_clone() {
|
||||
let state = Aerosol::new();
|
||||
state.obtain::<Arc<DummyNonClone>>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue