use std::{
any::{Any, TypeId, type_name},
cell::RefCell,
collections::{HashMap, HashSet},
fmt,
sync::{Arc, Mutex},
};
pub use xmacros::*;
pub mod middleware;
pub use middleware::Application;
mod http;
mod openapi;
pub use http::{ApiOperation, ApiResponse, HttpError, HttpStatus, ValidatedJson, ValidatedQuery};
pub use openapi::{DocumentationError, json_schema, openapi_document};
pub mod prelude {
pub use crate::{
Application, HttpError, HttpStatus, Injectable, controller, middleware, routes,
};
pub use axum::{
Json,
response::{IntoResponse, Response},
};
}
type DynValue = dyn Any + Send + Sync;
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),
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,
},
Downcast(&'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::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::Downcast(n) => write!(f, "provider {n} returned an incompatible value"),
Self::Poisoned => write!(f, "container lock was poisoned"),
}
}
}
impl std::error::Error for InjectError {}
pub struct ProviderDefinition {
key: ProviderKey,
scope: Scope,
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,
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 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 provide(container: &Container) -> Result<Self, InjectError>
where
Self: Sized;
fn definition() -> ProviderDefinition
where
Self: Sized,
{
ProviderDefinition::new(Self::scope(), Self::dependencies(), Self::provide)
}
}
#[derive(Default)]
pub struct Container {
providers: HashMap<ProviderKey, ProviderDefinition>,
singletons: Mutex<HashMap<ProviderKey, Arc<DynValue>>>,
}
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> {
match self.resolve::<T>() {
Ok(v) => Ok(Some(v)),
Err(InjectError::Missing(_)) => Ok(None),
Err(e) => Err(e),
}
}
fn resolve_key(&self, key: ProviderKey) -> Result<Arc<DynValue>, InjectError> {
let definition = self
.providers
.get(&key)
.ok_or(InjectError::Missing(key.name))?;
if definition.scope == Scope::Request {
return Err(InjectError::Missing(key.name));
}
if definition.scope == Scope::Singleton
&& let Some(value) = self
.singletons
.lock()
.map_err(|_| InjectError::Poisoned)?
.get(&key)
.cloned()
{
return Ok(value);
}
let value = (definition.factory)(self)?;
if definition.scope == Scope::Singleton {
self.singletons
.lock()
.map_err(|_| InjectError::Poisoned)?
.insert(key, value.clone());
}
Ok(value)
}
#[must_use]
pub fn request(&self) -> RequestContainer<'_> {
RequestContainer {
container: self,
values: HashMap::new(),
}
}
}
#[derive(Default)]
pub struct ContainerBuilder {
providers: 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())
}
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));
}
}
validate_provider_scopes(&providers)?;
validate_provider_cycles(&providers)?;
Ok(Container {
providers,
singletons: Mutex::new(HashMap::new()),
})
}
}
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() {
for dependency in &provider.dependencies {
if let Some(dependency) = providers.get(dependency)
&& provider.scope == Scope::Singleton
&& dependency.scope == Scope::Request
{
return Err(InjectError::InvalidScopeDependency {
owner: provider.key.name,
dependency: dependency.key.name,
});
}
}
}
Ok(())
}
pub struct RequestContainer<'a> {
container: &'a Container,
values: HashMap<ProviderKey, Arc<DynValue>>,
}
impl RequestContainer<'_> {
pub fn resolve<T: Send + Sync + 'static>(&mut self) -> Result<Arc<T>, InjectError> {
let key = ProviderKey::of::<T>();
let definition = self
.container
.providers
.get(&key)
.ok_or(InjectError::Missing(key.name))?;
let value = if definition.scope == Scope::Request {
if let Some(v) = self.values.get(&key) {
v.clone()
} else {
let v = (definition.factory)(self.container)?;
self.values.insert(key, v.clone());
v
}
} else {
self.container.resolve_key(key)?
};
value
.downcast::<T>()
.map_err(|_| InjectError::Downcast(type_name::<T>()))
}
}
#[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: HashMap<&'static str, ModuleDefinition>,
}
impl ModuleGraph {
#[must_use]
pub fn new(modules: Vec<ModuleDefinition>) -> Self {
Self {
modules: modules.into_iter().map(|m| (m.name, m)).collect(),
}
}
pub fn build(self) -> Result<Container, InjectError> {
for module in self.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 !self.modules.contains_key(import) {
return Err(InjectError::InvalidImport {
module: module.name,
import,
});
}
}
}
let mut builder = Container::builder();
for module in self.modules.into_values() {
for provider in module.providers {
builder = builder.provider(provider);
}
}
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()
));
}
}