fluxdi 1.1.0

FluxDI - Semi-Automatic Dependency Injector
Documentation

FluxDI - Semi-automatic Dependency Injector

Crates.io Documentation License Build Status

A lightweight, type-safe dependency injection container for Rust applications. FluxDI 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 via Arc / Rc
  • ๐Ÿ” 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
  • ๐Ÿงต Thread-Safe by Default: Uses Arc + RwLock for concurrent access
  • ๐Ÿ“ฆ Module System: Organize services into reusable modules
  • ๐Ÿ—๏ธ Enterprise Ready: Supports layered architecture, repository pattern, and use cases

๐Ÿ“ฆ Installation

Add this to your Cargo.toml:

[dependencies]

fluxdi = { path = "../fluxdi" }  # For local development

Or from crates.io (when published):

[dependencies]

fluxdi = "1.1.0"

๐Ÿš€ Quick Start

use fluxdi::{Injector, Provider, Shared, Module, Application};

// Define your services
struct DatabaseService {
    connection_string: String,
}

impl DatabaseService {
    fn new() -> Self {
        Self {
            connection_string: "postgresql://localhost:5432/myapp".to_string(),
        }
    }

    fn query(&self, sql: &str) -> String {
        format!("Executing '{}' on {}", sql, self.connection_string)
    }
}

struct UserService {
    db: Shared<DatabaseService>,
}

impl UserService {
    fn new(db: Shared<DatabaseService>) -> Self {
        Self { db }
    }

    fn create_user(&self, name: &str) -> String {
        self.db.query(&format!("INSERT INTO users (name) VALUES ('{}')", name))
    }
}

struct RootModule;

impl Module for RootModule {
    fn providers(&self, injector: &fluxdi::Injector) {
         // Register DatabaseService as singleton
        injector.provide::<DatabaseService>(Provider::root(|_| {
            Shared::new(DatabaseService::new())
        }));
        
        // Register UserService with DatabaseService dependency
        injector.provide::<UserService>(Provider::root(|inj| {
            let db = inj.resolve::<DatabaseService>();
            UserService::new(db).into()
        }));
    }
}

fn main() {
    // Create an application and register services
    let mut app = Application::new(RootModule);

    app.bootstrap_sync().expect("bootstrap failed");

    // Resolve and use services
    match app.injector().try_resolve::<UserService>() {
        Ok(user_service) => println!("{}", user_service.create_user("Alice")),
        Err(e) => eprintln!("Service resolution failed: {}", e),
    }

    // or just
    app.injector().resolve::<UserService>(); // This panics if not registered
}

๐Ÿ“š Documentation (mdBook)

FluxDI now includes an mdBook under docs/.

Build docs:

mdbook build docs

Serve locally:

mdbook serve docs --open

๏ฟฝ Examples

FluxDI includes ten comprehensive examples showcasing different use cases and patterns:

1. Basic Example

Location: examples/basic/

A simple introduction to FluxDI fundamentals:

  • Service registration with Injector and Provider
  • Transient and singleton lifetimes
  • Basic dependency resolution with try_resolve()
  • Error handling with Result types

Run:

cargo run --example basic

2. Complex Example (Advanced Patterns)

Location: examples/complex/

Demonstrates enterprise-grade architecture with:

  • Domain Layer: Clear entity definitions and repository interfaces
  • Application Layer: Use case pattern for business logic
  • Infrastructure Layer: SQLite persistence with concrete implementations
  • Dependency Injection: Multi-level service composition
  • Module System: Modular DI configuration with imported modules

Architecture:

core/
  โ”œโ”€โ”€ domain/       (User, Todo entities & repository traits)
  โ””โ”€โ”€ application/  (CreateUserUseCase, GetAllTodoUseCase, etc.)
infra/
  โ”œโ”€โ”€ di/           (Modules & dependency registration)
  โ””โ”€โ”€ persistence/  (SQLite repositories)

Run:

cd examples/complex

cargo run

Run Tests:

cd examples/complex

./test.sh

3. Actix-web Example

Location: examples/actix/

Minimal Actix-web integration with FluxDI extractors:

  • App-level injector state via injector_data(...)
  • Per-handler dependency resolution via fluxdi::actix::Resolved<T>
  • Zero-boilerplate extraction for services from the container

Run:

cd examples/actix

cargo run

Then request:

curl http://127.0.0.1:8081/hello

4. Axum REST API Example

Location: examples/axum/

Real-world REST API integration with Axum web framework:

  • HTTP handler functions with DI-resolved dependencies
  • Structured JSON responses with error handling
  • CRUD endpoints for Users and Todos
  • Service state management via InjectorState
  • Automatic per-handler DI resolution via Resolved<T> extractor
  • Dependency resolution per-request
  • Lifecycle-driven startup via Module::on_start + Application::bootstrap().await

Features:

  • POST /users - Create user
  • GET /users - List all users
  • GET /users/{id} - Get user by ID
  • DELETE /users/{id} - Delete user
  • POST /todos - Create todo
  • GET /todos - List all todos
  • PUT /todos/{id}/status - Update todo status
  • DELETE /todos/{id} - Delete todo

Run:

# Terminal 1: Start server

cd examples/axum

cargo run


# Terminal 2: Run comprehensive test suite

cd examples/axum

./test.sh

The test suite includes:

  • Server health checks
  • Sequential dependency extraction between requests
  • HTTP status code validation
  • JSON response parsing and assertion

5. Axum Lifecycle Example

Location: examples/axum-lifecycle/

Demonstrates module lifecycle startup with async hooks:

  • Register Router as DI service
  • Start axum::serve from Module::on_start(...)
  • Bootstrap with Application::bootstrap().await

Run:

cd examples/axum-lifecycle
cargo run

6. Module Sync Bootstrap Example

Location: examples/module-sync/

Minimal module-centric synchronous startup:

  • Uses Module::configure(...) as the single registration hook
  • Starts with Application::bootstrap_sync()

Run:

cd examples/module-sync
cargo run

7. Module Async Bootstrap Example

Location: examples/module-async/

Async instance registration and lifecycle orchestration:

  • Registers async instance via Provider::root_async(...)
  • Uses Application::bootstrap().await
  • Demonstrates lifecycle prewarm in on_start(...)

Run:

cd examples/module-async
cargo run

8. SeaORM SQLite Module Example

Location: examples/seaorm-sqlite/

SeaORM + SQLite integration through FluxDI module lifecycle:

  • Registers DatabaseConnection via Provider::root_async(...)
  • Uses Module::on_start(...) to run a startup connectivity check (SELECT 1)
  • Boots with Application::bootstrap().await

Run:

cd examples/seaorm-sqlite
cargo run

9. Dual HTTP Random-Port Example

Location: examples/dual-http-random-port/

Runs two HTTP services in one module lifecycle:

  • Randomly binds both services in 30000-65535
  • Starts both servers concurrently in on_start(...)
  • Gracefully aborts both tasks in on_stop(...)

Run:

cd examples/dual-http-random-port
cargo run

10. Mixed Sync + Async Module Example

Location: examples/module-mixed-sync-async/

Demonstrates sync and async providers in the same module:

  • Registers sync providers with Provider::root(...) and Provider::transient(...)
  • Registers async providers with Provider::root_async(...)
  • Uses sync resolve(...) and async try_resolve_async(...).await in the same startup flow

Run:

cd examples/module-mixed-sync-async
cargo run

๏ฟฝ๐Ÿ“– Usage Guide

Service Registration

Transient Services

Create new instances on each request:

use fluxdi::{Injector, Provider, Shared};
use uuid::Uuid;

struct LoggerService {
    session_id: String,
}

let injector = Injector::root();

// Transient: new instance each time (default behavior)
injector.provide::<LoggerService>(Provider::transient(|_| {
    Shared::new(LoggerService { 
        session_id: Uuid::new_v4().to_string() 
    })
}));

let logger1 = injector.resolve::<LoggerService>();
let logger2 = injector.resolve::<LoggerService>();
// logger1 and logger2 are different instances

Singleton Services

Create once and share across all dependents:

use fluxdi::{Injector, Provider, Shared};

struct ConfigService {
    app_name: String,
    debug: bool,
}

let injector = Injector::root();

// Singleton: same instance every time
injector.provide::<ConfigService>(Provider::root(|_| {
    Shared::new(ConfigService { 
        app_name: "MyApp".to_string(), 
        debug: true 
    })
}));

let config1 = injector.resolve::<ConfigService>();
let config2 = injector.resolve::<ConfigService>();
// config1 and config2 point to the same instance

Error Handling

FluxDI provides both panicking and non-panicking variants:

use fluxdi::{Injector, Provider, Shared, Error};

let injector = Injector::root();
injector.provide::<String>(Provider::transient(|_| Shared::new("Hello".to_string())));

// Non-panicking (try_resolve returns Result)
match injector.try_resolve::<String>() {
    Ok(s) => println!("Got: {}", s),
    Err(e) => println!("Error: {}", e),
}

// Trying to resolve an unregistered type
match injector.try_resolve::<u32>() {
    Ok(_) => unreachable!(),
    Err(e) => println!("Expected error: {}", e),
}

Dependency Injection

Services can depend on other services. Use module-based registration for clean organization:

use fluxdi::{Injector, Module, Provider, Shared};

struct DatabaseService { /* ... */ }
impl DatabaseService { fn new() -> Self { DatabaseService {} } }

struct CacheService { /* ... */ }
impl CacheService { fn new() -> Self { CacheService {} } }

struct UserRepository {
    db: Shared<DatabaseService>,
    cache: Shared<CacheService>,
}

impl UserRepository {
    fn new(db: Shared<DatabaseService>, cache: Shared<CacheService>) -> Self {
        Self { db, cache }
    }
}

// Define a module for persistence services
struct PersistenceModule;

impl Module for PersistenceModule {
    fn providers(&self, injector: &Injector) {
        injector.provide::<DatabaseService>(Provider::root(|_| {
            Shared::new(DatabaseService::new())
        }));
        
        injector.provide::<CacheService>(Provider::root(|_| {
            Shared::new(CacheService::new())
        }));
        
        injector.provide::<UserRepository>(Provider::root(|inj| {
            let db = inj.resolve::<DatabaseService>();
            let cache = inj.resolve::<CacheService>();
            UserRepository::new(db, cache).into()
        }));
    }
}

let injector = Injector::root();
let module = PersistenceModule;
module.providers(&injector);

let repo = injector.resolve::<UserRepository>();

๐Ÿ” Advanced Features

Circular Dependency Detection

FluxDI automatically detects and prevents circular dependencies by tracking resolution paths:

use fluxdi::{Injector, Provider, Shared};

// Example: attempting to create circular dependencies will fail
struct ServiceA {
    b: Shared<ServiceB>,
}

struct ServiceB {
    a: Shared<ServiceA>,
}

let injector = Injector::root();

// These registrations will create a circular dependency
// Attempting to resolve either service will result in an error
// Error: "Circular dependency detected in resolution path"

Resource Limits

You can constrain concurrent factory execution per provider:

use fluxdi::{Injector, Limits, Provider, Shared};

let injector = Injector::root();

injector.provide::<u32>(Provider::transient_with_limits(Limits::deny(2), |_| {
    Shared::new(42)
}));

Available policies:

  • Policy::Deny: fail immediately with ResourceLimitExceeded.
  • Policy::Block: wait for a free slot (supported when thread-safe is enabled).

Timeout support:

use std::time::Duration;
use fluxdi::{Limits, Provider, Shared};

injector.provide::<u32>(Provider::transient_with_limits(
    Limits::block_with_timeout(1, Duration::from_millis(50)),
    |_| Shared::new(42),
));

Async non-blocking wait (optional):

[dependencies]

fluxdi = { path = "../fluxdi", features = ["thread-safe", "async-factory", "resource-limit-async"] }

Tracing Integration

Enable the tracing feature for automatic logging:

[dependencies]

fluxdi = { path = "../fluxdi", features = ["tracing"] }

use fluxdi::{Application, Module, Provider, Shared};
use tracing::info;

struct MyModule;

impl Module for MyModule {
    fn providers(&self, injector: &fluxdi::Injector) {
        injector.provide::<DatabaseService>(Provider::root(|_| {
            info!("Registering DatabaseService");
            Shared::new(DatabaseService::new())
        }));
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let mut app = Application::new(MyModule);
    app.bootstrap().await.expect("bootstrap failed");
    
    // Resolving services will be traced when tracing feature is enabled
    let _db = app.injector().try_resolve::<DatabaseService>();
}

OpenTelemetry Bridge

Enable OpenTelemetry bridge support with the opentelemetry feature:

[dependencies]

fluxdi = { path = "../fluxdi", features = ["opentelemetry"] }

opentelemetry_sdk = { version = "0.29", features = ["trace"] }

use opentelemetry::trace::TracerProvider as _;
use opentelemetry_sdk::trace::SdkTracerProvider;
use fluxdi::opentelemetry_layer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

fn main() {
    // Configure exporter/processors on this provider in real applications.
    let tracer_provider = SdkTracerProvider::builder().build();
    let tracer = tracer_provider.tracer("my-service");

    tracing_subscriber::registry()
        .with(opentelemetry_layer(tracer))
        .try_init()
        .expect("failed to install tracing subscriber");

    // Keep provider ownership in your app lifecycle and shutdown gracefully.
    let _ = tracer_provider.shutdown();
}

fluxdi only provides tracing-to-OTel wiring helpers (opentelemetry_layer and try_init_opentelemetry) and does not auto-install exporters.

Service Metrics and Prometheus Export

Enable metrics collection:

[dependencies]

fluxdi = { path = "../fluxdi", features = ["metrics"] }

Enable Prometheus text export (prometheus implies metrics):

[dependencies]

fluxdi = { path = "../fluxdi", features = ["prometheus"] }

use fluxdi::{Injector, Provider, Shared};

fn main() {
    let injector = Injector::root();
    injector.provide::<u32>(Provider::root(|_| Shared::new(42)));

    let _ = injector.try_resolve::<u32>();

    let snapshot = injector.metrics_snapshot();
    println!("resolve attempts = {}", snapshot.resolve_attempts_total);

    #[cfg(feature = "prometheus")]
    println!("{}", injector.prometheus_metrics());
}

Async Factory Support

Enable async factories with the async-factory feature:

[dependencies]

fluxdi = { path = "../fluxdi", features = ["async-factory"] }

use fluxdi::{Injector, Provider, Shared};

#[tokio::main]
async fn main() {
    let injector = Injector::root();

    injector.provide::<String>(Provider::root_async(|_| async {
        Shared::new("async-value".to_string())
    }));

    let value = injector.resolve_async::<String>().await;
    assert_eq!(value.as_str(), "async-value");
}

Resolution strategy:

  • If a type is registered with Provider::*_async, resolve it with try_resolve_async / resolve_async.
  • Sync providers (Provider::root/singleton/transient) work with both sync and async resolve APIs.
  • Sync resolve against an async provider returns AsyncFactoryRequiresAsyncResolve.

Runnable example:

cd examples/async-factory

cargo run

Module Async Lifecycle Hooks

Module now supports async hooks for startup/shutdown orchestration:

  • configure(injector) as the primary, unified provider registration hook
  • providers_async(injector) as a legacy-compatible alias (new code should use configure)
  • on_start(injector) to start runtime resources (for example, an Axum listener)
  • on_stop(injector) to release resources in reverse module order

Use Application::bootstrap().await and Application::shutdown().await as the unified lifecycle APIs.

use fluxdi::{Application, Error, Injector, Module, Shared, module::ModuleLifecycleFuture};

struct WebModule;

impl Module for WebModule {
    fn on_start(&self, injector: Shared<Injector>) -> ModuleLifecycleFuture {
        Box::pin(async move {
            let app = injector.resolve::<axum::Router>();
            let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
                .await
                .map_err(|e| Error::module_lifecycle_failed("WebModule", "on_start", &e.to_string()))?;

            // Start in background so bootstrap() can return.
            tokio::spawn(async move {
                let _ = axum::serve(listener, app.as_ref().clone()).await;
            });
            Ok(())
        })
    }
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    let mut app = Application::new(WebModule);
    app.bootstrap().await
}

๐Ÿงช Testing

Unit Tests

Run the crate test suite:

# Run all tests for the workspace

cargo test


# Run tests for the fluxdi crate only

cargo test -p fluxdi


# Run with tracing feature

cargo test --features tracing


# Run with async factory feature

cargo test --features async-factory


# Run documentation tests

cargo test --doc -p fluxdi

๐Ÿ“ Project Structure

fluxdi/
โ”œโ”€โ”€ fluxdi/                 # FluxDI library crate
โ”‚   โ”œโ”€โ”€ src/              # core implementation (injector, provider, runtime)
โ”‚   โ””โ”€โ”€ README.md         # This file
โ”œโ”€โ”€ examples/
โ”‚   โ”œโ”€โ”€ basic/            # Basic usage example with simple DI
โ”‚   โ”œโ”€โ”€ complex/          # Advanced DI patterns with SQLite, repositories, use cases
โ”‚   โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ core/     # Domain (entities, use cases)
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ infra/    # Infrastructure (persistence, DI configuration)
โ”‚   โ”‚   โ””โ”€โ”€ test.sh       # Test script for complex example
โ”‚   โ”œโ”€โ”€ actix/            # Actix-web integration example
โ”‚   โ”‚   โ””โ”€โ”€ src/
โ”‚   โ”‚       โ””โ”€โ”€ main.rs   # Minimal Actix extractor usage
โ”‚   โ”œโ”€โ”€ axum-lifecycle/   # Axum startup in Module::on_start lifecycle hook
โ”‚   โ”‚   โ””โ”€โ”€ src/
โ”‚   โ”‚       โ””โ”€โ”€ main.rs   # Lifecycle-driven server bootstrap
โ”‚   โ”œโ”€โ”€ module-sync/      # Sync module bootstrap with configure()
โ”‚   โ”‚   โ””โ”€โ”€ src/
โ”‚   โ”‚       โ””โ”€โ”€ main.rs   # bootstrap_sync usage
โ”‚   โ”œโ”€โ”€ module-async/     # Async module bootstrap with async providers
โ”‚   โ”‚   โ””โ”€โ”€ src/
โ”‚   โ”‚       โ””โ”€โ”€ main.rs   # bootstrap().await + root_async provider
โ”‚   โ”œโ”€โ”€ module-mixed-sync-async/ # Sync and async providers in one module
โ”‚   โ”‚   โ””โ”€โ”€ src/
โ”‚   โ”‚       โ””โ”€โ”€ main.rs   # mixed resolve + try_resolve_async usage
โ”‚   โ”œโ”€โ”€ seaorm-sqlite/    # SeaORM + SQLite module lifecycle example
โ”‚   โ”‚   โ””โ”€โ”€ src/
โ”‚   โ”‚       โ””โ”€โ”€ main.rs   # on_start connectivity check (SELECT 1)
โ”‚   โ”œโ”€โ”€ dual-http-random-port/ # Two HTTP servers with random 30000-65535 ports
โ”‚   โ”‚   โ””โ”€โ”€ src/
โ”‚   โ”‚       โ””โ”€โ”€ main.rs   # concurrent startup in on_start + abort in on_stop
โ”‚   โ””โ”€โ”€ axum/             # REST API with Axum web framework
โ”‚       โ”œโ”€โ”€ src/
โ”‚       โ”‚   โ””โ”€โ”€ main.rs   # HTTP handlers with DI integration
โ”‚       โ””โ”€โ”€ test.sh       # Comprehensive API test suite
โ””โ”€โ”€ README.md

๐Ÿ”ง Configuration

Feature Flags

FluxDI exposes a small set of feature flags. See fluxdi/Cargo.toml for the authoritative list:

  • debug (enabled by default) โ€” enables extra debug formatting in selected types and errors.
  • thread-safe (optional) โ€” switches internal shared pointer and synchronization primitives to Arc + RwLock for concurrent access.
  • lock-free (optional) โ€” uses DashMap for provider/instance stores in thread-safe mode.
  • tracing (optional) โ€” enables tracing logs and spans during registration and resolution.
  • opentelemetry (optional) โ€” adds OpenTelemetry bridge helpers in fluxdi::observability.
  • metrics (optional) โ€” enables internal injector counters and Injector::metrics_snapshot().
  • prometheus (optional) โ€” enables Injector::prometheus_metrics() text export (includes metrics).
  • actix (optional) โ€” enables Actix-web integration helpers and extractors (and enables thread-safe).
  • axum (optional) โ€” enables Axum integration helpers and extractors (and enables thread-safe).
  • async-factory (optional) โ€” enables Provider::*_async and Injector::*_resolve_async APIs.
  • resource-limit-async (optional) โ€” uses Tokio semaphore for non-blocking async waits in resource limits.

The crate default is currently default = ["debug"]. If you need multithreaded use, enable thread-safe.

Environment Variables

When using the tracing feature, you can control logging levels:

# Set log level

RUST_LOG=debug cargo run --example basic


# Enable only FluxDI logs

RUST_LOG=fluxdi=info cargo run --example basic

๐Ÿค 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

  1. Clone the repository:
git clone https://github.com/DaiYuANg/fluxdi.git
cd fluxdi

  1. Run tests:
cargo test --all-features

  1. Check formatting:
cargo fmt --check

  1. Run clippy:
cargo clippy -- -D warnings

๐Ÿ“‹ Roadmap & TODO

๐Ÿงต Thread Safety

  • Arc-based Runtime: Thread-safe version of FluxDI using Arc instead of Rc (implemented behind the thread-safe feature)
  • Send + Sync Services: Support for Send + Sync services in thread-safe mode (enforced by API bounds)
  • Concurrent Access: Concurrent reads/writes supported via RwLock in thread-safe mode
  • Lock-free Operations: Optional lock-free feature (DashMap) for high-concurrency resolve paths

๐Ÿ”ง Advanced Features

  • Lazy Initialization: Singleton instances are created on first resolve
  • Service Metrics: Internal counters + timing totals via metrics_snapshot
  • Resource Limits: Per-provider concurrent creation limits with Limits + Policy

๐Ÿ“ฆ Ecosystem Integration

  • Async Factory Support: Async/await provider factories via Provider::*_async + Injector::*_resolve_async
  • Actix-web Integration: fluxdi::actix::{Resolved<T>, ServiceConfigExt, injector_data}
  • Axum Integration: Demonstrated with REST API example and state management
  • Axum Auto-Resolve Plugin: fluxdi::axum::Resolved<T> extractor for per-handler resolution
  • Rocket Integration: Layer and extractor support for Rocket web framework

๏ฟฝ๏ธ Architectural Patterns

  • Repository Pattern: Demonstrated in complex example with SQLite repositories
  • Layered Architecture: Clean separation of domain, application, and infrastructure layers
  • Use Case Pattern: Business logic encapsulated in use cases with DI
  • Web Framework Integration: Explored with Axum + Actix-web

๏ฟฝ๐Ÿ› ๏ธ Developer Experience

  • Derive Macros: Auto-generate factory functions from service structs (#[injectable])
  • Error Suggestions: Better error messages with fix suggestions

๐Ÿ“Š Observability

  • OpenTelemetry: Optional tracing bridge helpers via opentelemetry_layer / try_init_opentelemetry
  • Prometheus Metrics: prometheus_metrics() text export for scraping

๐ŸŽฏ 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

FluxDI - A semi-automatic dependency injection container for Rust
Repository: DaiYuANg/fluxdi
Made with โค๏ธ by the Binary Sea Team