Skip to content

Commit

Permalink
Implementations for Arc and other wrapping types.
Browse files Browse the repository at this point in the history
  • Loading branch information
Diggsey committed Jul 2, 2023
1 parent f8ebe14 commit e5f4977
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 26 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rust-analyzer.cargo.features": "all"
}
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
109 changes: 97 additions & 12 deletions src/async_constructible.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,78 @@
use std::error::Error;
use std::{error::Error, sync::Arc};

use async_trait::async_trait;

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.
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error>;
}

#[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> {
Expand All @@ -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)
}
}

Expand All @@ -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> {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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> {
Expand All @@ -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> {
Expand All @@ -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> {
Expand All @@ -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;
}
}
2 changes: 1 addition & 1 deletion src/axum.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
2 changes: 1 addition & 1 deletion src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
94 changes: 87 additions & 7 deletions src/sync_constructible.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,61 @@
use std::error::Error;
use std::{error::Error, sync::Arc};

use crate::{
resource::{unwrap_constructed, Resource},
slot::SlotDesc,
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> {
Expand All @@ -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>())
}
}

Expand All @@ -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> {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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> {
Expand All @@ -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>>();
}
}

0 comments on commit e5f4977

Please sign in to comment.