Skip to content
This repository was archived by the owner on Mar 15, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
04c7059
Change 'ServiceFactory::invoke' to take '&self'
TehPers May 27, 2021
f22ee87
Remove 'Service' constraint from 'ServiceFactory'
TehPers May 27, 2021
b08c660
Increment version number
TehPers Apr 7, 2022
d02dfa9
Add #[non_exhaustive] to error types
TehPers Apr 7, 2022
c47e050
Add unit tests
TehPers Apr 7, 2022
9f1eeb8
Change RequestInfo to use finish_non_exhaustive
TehPers Apr 7, 2022
afb1957
Add more upcasting/downcasting methods
TehPers Apr 7, 2022
3d0104d
Implement common traits for Arg<T>
TehPers Apr 7, 2022
98b50d1
Remove need to specify implementations of interfaces
TehPers Apr 11, 2022
bcf3aea
Update docs + test, get code working
TehPers Apr 13, 2022
79f3975
Remove AsAny, fix errors in "rc"
TehPers Apr 14, 2022
2ef6f1b
Update actix-web v3->v4, add docs and tests
TehPers Apr 16, 2022
e5451af
Update cycle detection, providers, and builders
TehPers Apr 17, 2022
ffd01c2
Fix clippy lints
TehPers Apr 17, 2022
d6f82cc
Fix more clippy errors
TehPers Apr 18, 2022
7b36c2b
Allow TypedProvider to have interface result type
TehPers Apr 26, 2022
ce96d1a
Update CI to actions/checkout@v3
TehPers Apr 26, 2022
696f6b2
Update with_arg() to be on provider/factory
TehPers Apr 26, 2022
5556ffa
Update some docs
TehPers Apr 26, 2022
f449449
Add generics/assoc types/where in interfaces
TehPers Apr 27, 2022
1b4e9a9
Move service factories to separate directory
TehPers Apr 27, 2022
a68d70f
Fix IoC docs and module macro
TehPers Apr 28, 2022
dd02dd1
Reformat code
TehPers Apr 28, 2022
66e23e9
Fix failed checks
TehPers Apr 28, 2022
9006005
Update InjectError to implement Service
TehPers Apr 29, 2022
bba43f1
Simplify macro
TehPers Apr 29, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup toolchain
uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -41,7 +41,7 @@ jobs:
features: rc
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup toolchain
uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -66,7 +66,7 @@ jobs:
- package: runtime_injector_actix
features: rc
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@clippy
- run: >-
cargo clippy
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[workspace]
members = ["crates/runtime_injector", "crates/runtime_injector_actix"]
resolver = "2"
members = ["crates/*"]
7 changes: 5 additions & 2 deletions crates/runtime_injector/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "runtime_injector"
version = "0.4.0"
version = "0.5.0"
authors = ["TehPers <tehperz@gmail.com>"]
edition = "2018"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Runtime dependency injection container"
repository = "https://github.com/TehPers/runtime_injector"
Expand All @@ -15,3 +15,6 @@ exclude = []
default = ["arc"]
arc = [] # Svc<T> = Arc<T>
rc = [] # Svc<T> = Rc<T>

[dependencies]
downcast-rs = "1"
20 changes: 0 additions & 20 deletions crates/runtime_injector/src/any.rs

This file was deleted.

152 changes: 123 additions & 29 deletions crates/runtime_injector/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,74 @@
use crate::{
Injector, Module, Provider, ProviderMap, RequestInfo, ServiceInfo,
provider_registry::{ProviderRegistry, ProviderRegistryType},
Injector, Interface, InterfaceRegistry, Module, Provider, RequestInfo,
Service, ServiceInfo, Svc,
};
use std::{
collections::{hash_map::Entry, HashMap},
fmt::Debug,
};

/// A builder for an [`Injector`].
#[derive(Default)]
#[derive(Debug, Default)]
pub struct InjectorBuilder {
providers: ProviderMap,
registry: InterfaceRegistryBuilder,
root_info: RequestInfo,
}

impl InjectorBuilder {
/// Assigns the provider for a service type. Multiple providers can be
/// registered for a service.
pub fn provide<P: Provider>(&mut self, provider: P) {
self.add_provider(Box::new(provider))
pub fn provide<P>(&mut self, provider: P)
where
P: Provider,
{
self.add_provider(Svc::new(provider));
}

/// Adds a provider to the injector.
#[allow(clippy::missing_panics_doc)]
pub fn add_provider(&mut self, provider: Box<dyn Provider>) {
// Should never panic
self.providers
.entry(provider.result())
.or_insert_with(|| Some(Vec::new()))
.as_mut()
.unwrap()
.push(provider)
pub fn add_provider<I>(
&mut self,
provider: Svc<dyn Provider<Interface = I>>,
) where
I: ?Sized + Interface,
{
self.registry
.ensure_providers_mut()
.add_provider_for(provider.result(), provider);
}

/// Removes all providers for a service type.
pub fn remove_providers(
&mut self,
service_info: ServiceInfo,
) -> Option<Vec<Box<dyn Provider>>> {
self.providers.remove(&service_info).flatten()
) -> Vec<Svc<dyn Provider<Interface = dyn Service>>> {
self.remove_providers_for::<dyn Service>(service_info)
}

/// Removes all providers for a service type from an interface.
pub fn remove_providers_for<I>(
&mut self,
service_info: ServiceInfo,
) -> Vec<Svc<dyn Provider<Interface = I>>>
where
I: ?Sized + Interface,
{
self.registry
.remove_providers_for::<I>(service_info)
.unwrap_or_default()
}

/// Clears all providers.
pub fn clear_providers(&mut self) {
self.registry.clear();
}

/// Clears all providers for an interface.
pub fn clear_providers_for<I>(&mut self)
where
I: ?Sized + Interface,
{
self.registry.remove_providers::<I>();
}

/// Borrows the root [`RequestInfo`] that will be used by calls to
Expand All @@ -57,26 +92,85 @@ impl InjectorBuilder {
/// module, they are overridden.
#[allow(clippy::missing_panics_doc)]
pub fn add_module(&mut self, module: Module) {
for (result, module_providers) in module.providers {
// Should never panic
let mut module_providers = module_providers.unwrap();
self.providers
.entry(result)
.and_modify(|providers| {
// Should never panic
providers.as_mut().unwrap().append(&mut module_providers)
})
.or_insert_with(|| Some(module_providers));
}
// Merge providers
self.registry.merge(module.registry);

// Merge parameters
for (key, value) in module.parameters {
drop(self.root_info_mut().insert_parameter_boxed(&key, value));
self.root_info_mut().insert_parameter_boxed(&key, value);
}
}

/// Builds the injector.
#[must_use]
pub fn build(self) -> Injector {
Injector::new_from_parts(self.providers, self.root_info)
Injector::new_from_parts(self.registry.build(), self.root_info)
}
}

#[derive(Debug, Default)]
pub(crate) struct InterfaceRegistryBuilder {
registries: HashMap<ServiceInfo, Box<dyn ProviderRegistryType>>,
}

impl InterfaceRegistryBuilder {
pub fn ensure_providers_mut<I>(&mut self) -> &mut ProviderRegistry<I>
where
I: ?Sized + Interface,
{
self.registries
.entry(ServiceInfo::of::<I>())
.or_insert_with(|| Box::new(ProviderRegistry::<I>::default()))
.downcast_mut()
.unwrap()
}

pub fn remove_providers<I>(&mut self) -> Option<ProviderRegistry<I>>
where
I: ?Sized + Interface,
{
let interface_info = ServiceInfo::of::<I>();
let registry = self.registries.remove(&interface_info)?;
let registry = registry.downcast().unwrap();
Some(*registry)
}

pub fn remove_providers_for<I>(
&mut self,
service_info: ServiceInfo,
) -> Option<Vec<Svc<dyn Provider<Interface = I>>>>
where
I: ?Sized + Interface,
{
let registry = self.registries.get_mut(&service_info)?;
let registry: &mut ProviderRegistry<I> =
registry.downcast_mut().unwrap();
registry.remove_providers_for(service_info)
}

pub fn clear(&mut self) {
self.registries.clear();
}

pub fn merge(&mut self, other: InterfaceRegistryBuilder) {
for (interface_info, other_providers) in other.registries {
match self.registries.entry(interface_info) {
Entry::Occupied(entry) => {
entry.into_mut().merge(other_providers).unwrap();
}
Entry::Vacant(entry) => {
entry.insert(other_providers);
}
}
}
}

pub fn build(self) -> InterfaceRegistry {
let registries = self
.registries
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect();
InterfaceRegistry::new(registries)
}
}
Loading