15 个版本 (1 个稳定版)

1.0.0 2024年7月5日
1.0.0-alpha.82023年8月14日
1.0.0-alpha.32023年7月2日
0.3.0 2020年6月2日
0.1.2 2018年10月29日

#266Rust 模式

Download history 286/week @ 2024-05-02 494/week @ 2024-05-09 712/week @ 2024-05-16 554/week @ 2024-05-23 387/week @ 2024-05-30 242/week @ 2024-06-06 651/week @ 2024-06-13 1010/week @ 2024-06-20 320/week @ 2024-06-27 768/week @ 2024-07-04 433/week @ 2024-07-11 786/week @ 2024-07-18 625/week @ 2024-07-25 468/week @ 2024-08-01 653/week @ 2024-08-08 610/week @ 2024-08-15

每月下载量:2,554

MIT/Apache

55KB
1K SLoC

气溶胶 GHA 构建状态

简单的 Rust 依赖注入

文档


lib.rs:

气溶胶

简单而强大的 Rust 依赖注入。

这个包提供了 Aero 类型,用于根据类型存储依赖项(称为资源)。资源可以在应用程序启动时急切地构建,或者在首次需要时按需构建。资源可以在创建时访问和/或初始化其他资源。

该包将检测依赖循环(如果构建资源 A 需要 B,而 B 本身又需要 A),在这种情况下会引发 panic 而不是栈溢出。

Aero 类型有一个可选的类型参数,用于使某些资源 必需。当资源必需时,可以可靠地访问它。存在一个名为 Aero![...] 的宏,可以轻松地为具有特定必需资源集的 Aero 命名。

克隆或类型转换 Aero 类型成本低(相当于克隆 Arc)。

可选功能

async

允许异步构建资源,并提供相应的 AsyncConstructibleResource 特性。

axum

提供与 axum 网络框架的集成。有关更多信息,请参阅 axum 模块。

示例用法

use std::{sync::Arc, any::Any};

use aerosol::{Aero, Constructible};

// Here, we can list all the things we want to guarantee are in
// our app state. This is entirely optional, we could also just
// use the `Aero` type with default arguments and check that
// resources are present at runtime.
type AppState = Aero![
    Arc<PostmarkClient>,
    Arc<dyn EmailSender>,
    ConnectionPool,
    MessageQueue,
    MagicNumber,
];

fn main() {
    let app_state: AppState = Aero::new()
        // Directly add a resource which doesn't implement `Constructible`.
        .with(MagicNumber(42))
        // Construct an `Arc<PostmarkClient>` resource in the AppState
        .with_constructed::<Arc<PostmarkClient>>()
        // Check that an implementation of `EmailSender` was added as a result
        .assert::<Arc<dyn EmailSender>>()
        // Automatically construct anything else necessary for our AppState
        // (in this case, `ConnectionPool` and `MessageQueue`)
        .construct_remaining();

    // Add an extra resource
    app_state.insert("Hello, world");

    run(app_state);
}

fn run(app_state: AppState) {
    // The `get()` method is infallible because the `Arc<dyn EmailSender>` was
    // explicitly listed when defining our `AppState`.
    let email_sender: Arc<dyn EmailSender> = app_state.get();
    email_sender.send(/* email */);

    // We have to use `try_get()` here because a `&str` is not guaranteed to
    // exist on our `AppState`.
    let hello_message: &str = app_state.try_get().unwrap();
    println!("{hello_message}");

    // ... more application logic
}

// The `Constructible` trait can be implemented to allow resources to be automatically
// constructed.
impl Constructible for PostmarkClient {
    type Error = anyhow::Error;

    fn construct(aero: &Aero) -> Result<Self, Self::Error> {
        PostmarkClient::new(/* initialize using environment variables */)
    }

    fn after_construction(this: &(dyn Any + Send + Sync), aero: &Aero) -> Result<(), Self::Error> {
        // We can use this to automatically populate extra resources on the context.
        // For example, in this case we can make it so that if an `Arc<PostmarkClient>` gets
        // constructed, we also provide `Arc<dyn EmailSender>`.
        if let Some(arc) = this.downcast_ref::<Arc<Self>>() {
            aero.insert(arc.clone() as Arc<dyn EmailSender>)
        }
        Ok(())
    }
}

impl Constructible for ConnectionPool {
    type Error = anyhow::Error;
    fn construct(aero: &Aero) -> Result<Self, Self::Error> {
        // ...
    }
}

impl Constructible for MessageQueue {
    type Error = anyhow::Error;
    fn construct(aero: &Aero) -> Result<Self, Self::Error> {
        // ...
    }
}

实现细节

Aero 类型管理从资源类型到“插槽”的映射的共享所有权。对于给定的资源类型,相应的“插槽”可以处于以下三种状态之一

  1. 不存在。在映射中不存在此资源的实例。
  2. 存在。在映射中存在此资源的实例,可以立即访问。
  3. 构建中。此资源的实例目前正在构建中,构建完成后可以访问。插槽维护一个等待此资源构建的线程或任务列表,并在资源可用时唤醒它们。

资源可以同步构建,也可以(当功能启用时)异步构建。

如果资源在构建过程中被访问,调用者将等待构建完成。调用者根据是否使用 obtain()obtain_async() 来确定等待是同步还是异步。

在任务中对正在异步构建的资源同步等待,或者在线程中对正在同步构建的资源异步等待,是可能(并且允许)的。

依赖关系

~1.9–9.5MB
~87K SLoC