use std::{
any::{Any, TypeId, type_name},
cell::RefCell,
collections::{HashMap, HashSet},
fmt,
sync::{Arc, Mutex, OnceLock},
};
pub use xmacros::*;
pub mod middleware;
pub use middleware::Application;
#[cfg(feature = "upload")]
pub mod upload;
#[cfg(feature = "testing")]
pub mod testing;
pub mod config;
mod scoped;
pub use scoped::{Inject, with_container};
pub mod exceptions;
mod http;
mod openapi;
pub mod versioning;
pub use http::{ApiOperation, ApiResponse, HttpError, HttpStatus, ValidatedJson, ValidatedQuery};
pub use openapi::{DocumentationError, json_schema, openapi_document};
pub use versioning::{ApiVersion, ApiVersions, VersionError};
pub mod prelude {
pub use crate::{
Application, HttpError, HttpStatus, Inject, Injectable, controller, middleware, routes,
};
pub use axum::{
Json,
response::{IntoResponse, Response},
};
}
type DynValue = dyn Any + Send + Sync;
type Cache = Mutex<HashMap<ProviderKey, Arc<OnceLock<Result<Arc<DynValue>, InjectError>>>>>;
type Factory = dyn Fn(&Container) -> Result<Arc<DynValue>, InjectError> + Send + Sync;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Scope {
#[default]
Singleton,
Transient,
Request,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ProviderKey {
type_id: TypeId,
name: &'static str,
}
impl ProviderKey {
#[must_use]
pub fn of<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
name: type_name::<T>(),
}
}
#[must_use]
pub const fn name(self) -> &'static str {
self.name
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum InjectError {
Missing(&'static str),
DuplicateModule(&'static str),
DuplicateProvider(&'static str),
PrivateProviderAccess {
provider: &'static str,
module: &'static str,
},
InvalidExport {
provider: &'static str,
module: &'static str,
},
InvalidImport {
module: &'static str,
import: &'static str,
},
Circular(Vec<&'static str>),
InvalidScopeDependency {
owner: &'static str,
dependency: &'static str,
},
RequestScopeRequired(&'static str),
UndeclaredDependency {
owner: &'static str,
dependency: &'static str,
},
Downcast(&'static str),
ConstructionFailed(&'static str),
Poisoned,
}
impl fmt::Display for InjectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Missing(n) => write!(f, "missing provider: {n}"),
Self::DuplicateModule(n) => write!(f, "duplicate module: {n}"),
Self::DuplicateProvider(n) => write!(f, "duplicate provider: {n}"),
Self::PrivateProviderAccess { provider, module } => write!(
f,
"private provider {provider} is not visible to module {module}"
),
Self::InvalidExport { provider, module } => write!(
f,
"module {module} exports unregistered provider {provider}"
),
Self::InvalidImport { module, import } => {
write!(f, "module {module} imports unknown module {import}")
}
Self::Circular(chain) => write!(f, "circular dependency:\n{}", chain.join("\n -> ")),
Self::InvalidScopeDependency { owner, dependency } => write!(
f,
"invalid scope dependency: singleton {owner} depends on request-scoped {dependency}"
),
Self::RequestScopeRequired(n) => write!(f, "request scope required for provider: {n}"),
Self::UndeclaredDependency { owner, dependency } => write!(
f,
"provider {owner} resolved undeclared dependency {dependency}"
),
Self::Downcast(n) => write!(f, "provider {n} returned an incompatible value"),
Self::ConstructionFailed(reason) => write!(f, "provider construction failed: {reason}"),
Self::Poisoned => write!(f, "container lock was poisoned"),
}
}
}
impl std::error::Error for InjectError {}
pub struct ProviderDefinition {
key: ProviderKey,
scope: Scope,
dependencies: Vec<ProviderKey>,
optional_dependencies: Vec<ProviderKey>,
factory: Arc<Factory>,
}
impl fmt::Debug for ProviderDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProviderDefinition")
.field("key", &self.key)
.field("scope", &self.scope)
.field("dependencies", &self.dependencies)
.finish()
}
}
impl ProviderDefinition {
#[must_use]
pub fn new<T, F>(scope: Scope, dependencies: Vec<ProviderKey>, factory: F) -> Self
where
T: Send + Sync + 'static,
F: Fn(&Container) -> Result<T, InjectError> + Send + Sync + 'static,
{
Self {
key: ProviderKey::of::<T>(),
scope,
dependencies,
optional_dependencies: Vec::new(),
factory: Arc::new(move |c| factory(c).map(|v| Arc::new(v) as Arc<DynValue>)),
}
}
#[must_use]
pub fn singleton<T, F>(dependencies: Vec<ProviderKey>, factory: F) -> Self
where
T: Send + Sync + 'static,
F: Fn(&Container) -> Result<T, InjectError> + Send + Sync + 'static,
{
Self::new(Scope::Singleton, dependencies, factory)
}
#[must_use]
pub fn request<T, F>(dependencies: Vec<ProviderKey>, factory: F) -> Self
where
T: Send + Sync + 'static,
F: Fn(&Container) -> Result<T, InjectError> + Send + Sync + 'static,
{
Self::new(Scope::Request, dependencies, factory)
}
#[must_use]
pub fn transient<T, F>(dependencies: Vec<ProviderKey>, factory: F) -> Self
where
T: Send + Sync + 'static,
F: Fn(&Container) -> Result<T, InjectError> + Send + Sync + 'static,
{
Self::new(Scope::Transient, dependencies, factory)
}
#[must_use]
pub fn value<T: Send + Sync + 'static>(value: T) -> Self {
let value: Arc<DynValue> = Arc::new(value);
Self {
key: ProviderKey::of::<T>(),
scope: Scope::Singleton,
dependencies: Vec::new(),
optional_dependencies: Vec::new(),
factory: Arc::new(move |_| Ok(value.clone())),
}
}
#[must_use]
pub fn optional_dependency<T: 'static>(mut self) -> Self {
self.optional_dependencies.push(ProviderKey::of::<T>());
self
}
#[must_use]
pub fn key(&self) -> ProviderKey {
self.key
}
#[must_use]
pub fn scope(&self) -> Scope {
self.scope
}
#[must_use]
pub fn dependencies(&self) -> &[ProviderKey] {
&self.dependencies
}
}
pub trait Provider: Send + Sync + 'static {
fn scope() -> Scope {
Scope::Singleton
}
fn dependencies() -> Vec<ProviderKey> {
Vec::new()
}
fn optional_dependencies() -> Vec<ProviderKey> {
Vec::new()
}
fn provide(container: &Container) -> Result<Self, InjectError>
where
Self: Sized;
fn definition() -> ProviderDefinition
where
Self: Sized,
{
let mut definition =
ProviderDefinition::new(Self::scope(), Self::dependencies(), Self::provide);
definition.optional_dependencies = Self::optional_dependencies();
definition
}
}
#[derive(Clone, Default)]
pub struct Container {
providers: Arc<HashMap<ProviderKey, ProviderDefinition>>,
singletons: Arc<Cache>,
request_values: Option<Arc<Cache>>,
}
thread_local! {
static RESOLVING: RefCell<Vec<(usize, ProviderKey)>> = const { RefCell::new(Vec::new()) };
}
struct ResolutionGuard;
impl Drop for ResolutionGuard {
fn drop(&mut self) {
RESOLVING.with(|path| {
path.borrow_mut().pop();
});
}
}
impl Container {
#[must_use]
pub fn builder() -> ContainerBuilder {
ContainerBuilder::default()
}
pub fn resolve<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, InjectError> {
self.resolve_key(ProviderKey::of::<T>())?
.downcast::<T>()
.map_err(|_| InjectError::Downcast(type_name::<T>()))
}
pub fn resolve_optional<T: Send + Sync + 'static>(
&self,
) -> Result<Option<Arc<T>>, InjectError> {
if !self.providers.contains_key(&ProviderKey::of::<T>()) {
return Ok(None);
}
self.resolve().map(Some)
}
fn resolve_key(&self, key: ProviderKey) -> Result<Arc<DynValue>, InjectError> {
let definition = self
.providers
.get(&key)
.ok_or(InjectError::Missing(key.name))?;
let identity = Arc::as_ptr(&self.providers) as usize;
RESOLVING.with(|path| {
let mut path = path.borrow_mut();
if let Some(index) = path.iter().position(|entry| *entry == (identity, key)) {
let mut chain = path[index..]
.iter()
.map(|entry| entry.1.name)
.collect::<Vec<_>>();
chain.push(key.name);
return Err(InjectError::Circular(chain));
}
if let Some((id, parent)) = path.last()
&& *id == identity
&& !self.providers[parent].dependencies.contains(&key)
{
return Err(InjectError::UndeclaredDependency {
owner: parent.name,
dependency: key.name,
});
}
path.push((identity, key));
Ok(())
})?;
let _guard = ResolutionGuard;
let cache = match definition.scope {
Scope::Transient => return (definition.factory)(self),
Scope::Singleton => &self.singletons,
Scope::Request => self
.request_values
.as_ref()
.ok_or(InjectError::RequestScopeRequired(key.name))?,
};
let slot = cache
.lock()
.map_err(|_| InjectError::Poisoned)?
.entry(key)
.or_default()
.clone();
slot.get_or_init(|| {
if definition.scope == Scope::Singleton {
let root = Self {
providers: self.providers.clone(),
singletons: self.singletons.clone(),
request_values: None,
};
(definition.factory)(&root)
} else {
(definition.factory)(self)
}
})
.clone()
}
#[must_use]
pub fn request(&self) -> RequestScope {
RequestScope {
container: Self {
providers: self.providers.clone(),
singletons: self.singletons.clone(),
request_values: Some(Arc::default()),
},
}
}
pub fn initialize(&self) -> Result<(), InjectError> {
for definition in self
.providers
.values()
.filter(|definition| definition.scope == Scope::Singleton)
{
self.resolve_key(definition.key)?;
}
Ok(())
}
}
#[derive(Default)]
pub struct ContainerBuilder {
providers: Vec<ProviderDefinition>,
overrides: Vec<ProviderDefinition>,
}
impl ContainerBuilder {
#[must_use]
pub fn provider(mut self, provider: ProviderDefinition) -> Self {
self.providers.push(provider);
self
}
#[must_use]
pub fn injectable<T: Provider>(self) -> Self {
self.provider(T::definition())
}
#[must_use]
pub fn override_provider(mut self, provider: ProviderDefinition) -> Self {
self.overrides.push(provider);
self
}
#[must_use]
pub fn value<T: Send + Sync + 'static>(self, value: T) -> Self {
self.provider(ProviderDefinition::value(value))
}
#[cfg(feature = "testing")]
pub(crate) fn into_definitions(self) -> Vec<ProviderDefinition> {
self.providers
}
pub fn build(self) -> Result<Container, InjectError> {
let mut providers = HashMap::new();
for provider in self.providers {
let key = provider.key;
if providers.insert(key, provider).is_some() {
return Err(InjectError::DuplicateProvider(key.name));
}
}
let mut overrides = HashSet::new();
for replacement in self.overrides {
let key = replacement.key;
if !overrides.insert(key) {
return Err(InjectError::DuplicateProvider(key.name));
}
if !providers.contains_key(&key) {
return Err(InjectError::Missing(key.name));
}
providers.insert(key, replacement);
}
for provider in providers.values() {
for dependency in &provider.dependencies {
if !providers.contains_key(dependency)
&& !provider.optional_dependencies.contains(dependency)
{
return Err(InjectError::Missing(dependency.name));
}
}
}
validate_provider_scopes(&providers)?;
validate_provider_cycles(&providers)?;
Ok(Container {
providers: Arc::new(providers),
singletons: Arc::default(),
request_values: None,
})
}
}
fn validate_provider_cycles(
providers: &HashMap<ProviderKey, ProviderDefinition>,
) -> Result<(), InjectError> {
fn visit(
key: ProviderKey,
providers: &HashMap<ProviderKey, ProviderDefinition>,
visiting: &mut Vec<ProviderKey>,
visited: &mut HashSet<ProviderKey>,
) -> Result<(), InjectError> {
if let Some(index) = visiting.iter().position(|entry| *entry == key) {
let mut chain = visiting[index..]
.iter()
.map(|entry| entry.name)
.collect::<Vec<_>>();
chain.push(key.name);
return Err(InjectError::Circular(chain));
}
if !visited.insert(key) {
return Ok(());
}
visiting.push(key);
if let Some(provider) = providers.get(&key) {
for dependency in &provider.dependencies {
if providers.contains_key(dependency) {
visit(*dependency, providers, visiting, visited)?;
}
}
}
visiting.pop();
Ok(())
}
let mut visiting = Vec::new();
let mut visited = HashSet::new();
for key in providers.keys().copied() {
visit(key, providers, &mut visiting, &mut visited)?;
}
Ok(())
}
fn validate_provider_scopes(
providers: &HashMap<ProviderKey, ProviderDefinition>,
) -> Result<(), InjectError> {
for provider in providers
.values()
.filter(|provider| provider.scope == Scope::Singleton)
{
let mut pending = provider.dependencies.clone();
let mut visited = HashSet::new();
while let Some(key) = pending.pop() {
if !visited.insert(key) {
continue;
}
if let Some(dependency) = providers.get(&key) {
if dependency.scope == Scope::Request {
return Err(InjectError::InvalidScopeDependency {
owner: provider.key.name,
dependency: key.name,
});
}
pending.extend_from_slice(&dependency.dependencies);
}
}
}
Ok(())
}
#[derive(Clone)]
pub struct RequestScope {
container: Container,
}
impl RequestScope {
pub(crate) fn belongs_to(&self, container: &Container) -> bool {
Arc::ptr_eq(&self.container.providers, &container.providers)
}
pub fn resolve<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, InjectError> {
self.container.resolve()
}
pub fn resolve_optional<T: Send + Sync + 'static>(
&self,
) -> Result<Option<Arc<T>>, InjectError> {
self.container.resolve_optional()
}
}
pub type RequestContainer<'a> = RequestScope;
#[derive(Debug)]
pub struct ModuleDefinition {
pub name: &'static str,
pub providers: Vec<ProviderDefinition>,
pub imports: Vec<&'static str>,
pub exports: Vec<ProviderKey>,
pub global: bool,
}
impl ModuleDefinition {
#[must_use]
pub fn new(name: &'static str) -> Self {
Self {
name,
providers: Vec::new(),
imports: Vec::new(),
exports: Vec::new(),
global: false,
}
}
#[must_use]
pub fn provider(mut self, provider: ProviderDefinition) -> Self {
self.providers.push(provider);
self
}
#[must_use]
pub fn import(mut self, module: &'static str) -> Self {
self.imports.push(module);
self
}
#[must_use]
pub fn export<T: 'static>(mut self) -> Self {
self.exports.push(ProviderKey::of::<T>());
self
}
#[must_use]
pub fn global(mut self) -> Self {
self.global = true;
self
}
}
pub struct ModuleGraph {
modules: Vec<ModuleDefinition>,
}
impl ModuleGraph {
#[must_use]
pub fn new(modules: Vec<ModuleDefinition>) -> Self {
Self { modules }
}
pub fn build(self) -> Result<Container, InjectError> {
self.build_with_overrides(Vec::new())
}
pub fn build_with_overrides(
self,
overrides: Vec<ProviderDefinition>,
) -> Result<Container, InjectError> {
let mut modules = HashMap::new();
for module in self.modules {
let name = module.name;
if modules.insert(name, module).is_some() {
return Err(InjectError::DuplicateModule(name));
}
}
for module in modules.values() {
let owned: HashSet<_> = module
.providers
.iter()
.map(ProviderDefinition::key)
.collect();
for export in &module.exports {
if !owned.contains(export) {
return Err(InjectError::InvalidExport {
provider: export.name,
module: module.name,
});
}
}
for import in &module.imports {
if !modules.contains_key(import) {
return Err(InjectError::InvalidImport {
module: module.name,
import,
});
}
}
}
for module in modules.values() {
let mut visible: HashSet<_> = module
.providers
.iter()
.map(ProviderDefinition::key)
.collect();
for imported in modules
.values()
.filter(|candidate| candidate.global || module.imports.contains(&candidate.name))
{
visible.extend(imported.exports.iter().copied());
}
for provider in &module.providers {
for dependency in &provider.dependencies {
let registered = modules.values().any(|candidate| {
candidate
.providers
.iter()
.any(|provider| provider.key == *dependency)
});
if registered && !visible.contains(dependency) {
return Err(InjectError::PrivateProviderAccess {
provider: dependency.name,
module: module.name,
});
}
}
}
}
let mut builder = Container::builder();
for module in modules.into_values() {
for provider in module.providers {
builder = builder.provider(provider);
}
}
for replacement in overrides {
builder = builder.override_provider(replacement);
}
builder.build()
}
}
pub trait Injectable: Sized {
fn inject(injector: &Injector) -> Result<Self, InjectError>;
}
impl<T: Injectable + 'static> Injectable for Arc<T> {
fn inject(injector: &Injector) -> Result<Self, InjectError> {
injector.resolve::<T>().map(Arc::new)
}
}
pub struct Injector {
values: HashMap<TypeId, Box<dyn Any>>,
}
thread_local! { static PATH: RefCell<Vec<&'static str>> = const { RefCell::new(Vec::new()) }; }
impl Injector {
#[must_use]
pub fn new() -> Self {
Self {
values: HashMap::new(),
}
}
#[must_use]
pub fn value<T: Clone + 'static>(mut self, value: T) -> Self {
self.values.insert(TypeId::of::<T>(), Box::new(value));
self
}
pub fn resolve<T: Injectable + 'static>(&self) -> Result<T, InjectError> {
let name = type_name::<T>();
PATH.with(|path| {
let mut path = path.borrow_mut();
if let Some(index) = path.iter().position(|entry| *entry == name) {
let mut chain = path[index..].to_vec();
chain.push(name);
return Err(InjectError::Circular(chain));
}
path.push(name);
drop(path);
let result = T::inject(self);
PATH.with(|path| {
path.borrow_mut().pop();
});
result
})
}
pub fn value_of<T: Clone + 'static>(&self) -> Result<T, InjectError> {
self.values
.get(&TypeId::of::<T>())
.and_then(|value| value.downcast_ref::<T>())
.cloned()
.ok_or(InjectError::Missing(type_name::<T>()))
}
}
impl Default for Injector {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct A;
impl Provider for A {
fn provide(_: &Container) -> Result<Self, InjectError> {
Ok(Self)
}
}
#[test]
fn singleton_is_cached() {
let c = Container::builder().injectable::<A>().build().unwrap();
assert!(Arc::ptr_eq(
&c.resolve::<A>().unwrap(),
&c.resolve::<A>().unwrap()
));
}
}