SaDi - Semi-automatic Dependency Injector
A lightweight, type-safe dependency injection container for Rust applications. SaDi provides ergonomic service registration (including trait-object bindings), transient and singleton lifetimes, semi-automatic dependency resolution, and circular dependency detection.
โจ Features
- ๐ Type-Safe: Leverages Rust's type system for compile-time safety
- ๐ Transient Services: Create new instances on each request
- ๐ Singleton Services: Shared instances with reference counting
- ๐ Circular Detection: Prevents infinite loops in dependency graphs
- โ Error Handling: Comprehensive error types with detailed messages
- ๐ Optional Logging: Tracing integration with feature gates
- ๐ Zero-Cost Abstractions: Feature gates enable compile-time optimization
๐ฆ Installation
Add this to your Cargo.toml:
[]
= "0.2.1"
๐ Quick Start
use ;
use Rc;
// Define your services (non-thread-safe default uses `Rc` via `Shared`)
๐ Usage Guide
Service Registration
Transient Services
Create new instances on each request. The default bind registration is transient:
use ;
use Uuid;
let c = container! ;
let logger1 = c..unwrap;
let logger2 = c..unwrap;
Singleton Services
Create once and share across all dependents. Use the singleton annotation in bind:
use ;
let c = container! ;
let config1 = c..unwrap;
let config2 = c..unwrap;
assert!;
Error Handling
SaDi provides both panicking and non-panicking variants:
use ;
let c = new;
c..unwrap;
// Resolve (panicking)
let service = c..unwrap;
// Non-panicking
match c.
// Trying to resolve an unregistered type
match c.
Dependency Injection
Services can depend on other services. Use the container! macro to register bindings concisely:
use ;
let c = container! ;
let repo = c..unwrap;
๐ Advanced Features
Circular Dependency Detection
SaDi automatically detects and prevents circular dependencies:
use Container;
// Example: registering circular dependencies will produce a descriptive error at runtime
let c = new;
// c.bind_concrete::<ServiceA, ServiceA, _>(|c| { let _ = c.resolve::<ServiceB>(); ServiceA });
// c.bind_concrete::<ServiceB, ServiceB, _>(|c| { let _ = c.resolve::<ServiceA>(); ServiceB });
match c.
Tracing Integration
Enable the tracing feature for automatic logging (the crate's default feature includes tracing):
[]
= { = "0.2.1", = ["tracing"] }
use ;
use info;
async
๐งช Testing
Run the test suite:
# Run all tests for the workspace
# Run tests for the sadi crate only
# Run with tracing feature
# Run documentation tests
# Run example
๐ Project Structure
sadi/
โโโ sadi/ # library crate
โ โโโ src/ # core implementation (container, macros, types)
โโโ examples/
โ โโโ basic/ # Comprehensive usage example
โโโ README.md # This file
๐ง Configuration
Feature Flags
SaDi exposes a small set of feature flags. See sadi/Cargo.toml for the authoritative list, but the crate currently defines:
thread-safe(enabled by default) โ switches internal shared pointer and synchronization primitives toArc+RwLock/Mutexfor thread-safe containers.tracing(enabled by default) โ integrates with thetracingcrate to emit logs during registration/resolution.
The workspace default enables both thread-safe and tracing. To opt out of thread-safe behavior (use Rc instead of Arc), disable the thread-safe feature.
Environment Variables
When using the tracing feature, you can control logging levels:
# Set log level
RUST_LOG=debug
# Enable only SaDi logs
RUST_LOG=sadi=info
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Development Setup
- Clone the repository:
- Run tests:
- Check formatting:
- Run clippy:
๐ Roadmap & TODO
๐งต Thread Safety
- Arc-based Container: Thread-safe version of SaDi using
Arcinstead ofRc(implemented behind thethread-safefeature) - Send + Sync Services: Support for
Send + Syncservices in thread-safe mode (enforced by API bounds) - Concurrent Access: Concurrent reads/writes supported via
RwLock/Mutexin thread-safe mode - Lock-free Operations: Minimize contention in high-concurrency scenarios
๐ง Advanced Features
- Lazy Initialization: Singleton instances are created on first
provide(implemented inFactory) - Service Metrics: Internal container metrics for observability (resolution counts, timing)
๐ฆ Ecosystem Integration
- Async Factory Support: Enable async/await in factory functions for Tokio/async-std runtimes
- Actix-web Integration: Extension trait and extractors for Actix-web framework
- Axum Integration: Layer and extractor support for Axum web framework
- Rocket Integration: Layer and extractor support for Rocket web framework
๐ ๏ธ Developer Experience
- Derive Macros: Auto-generate factory functions from service structs (
#[injectable]) - Error Suggestions: Better error messages with fix suggestions
๐ Observability
- OpenTelemetry: Built-in telemetry and distributed tracing
- Prometheus Metrics: Expose container metrics for monitoring
๐ฏ Performance
- Memory Optimization: Reduced memory footprint for large containers
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Inspired by dependency injection patterns from other languages and frameworks
- Built with โค๏ธ using Rust's powerful type system
- Thanks to the Rust community for excellent crates and documentation
Made with โค๏ธ by Joรฃo Pedro Martins