7个版本
0.2.2 | 2021年8月27日 |
---|---|
0.2.1 | 2021年8月26日 |
0.1.0 | 2021年1月4日 |
0.0.4 | 2020年12月21日 |
#1319 在 开发工具 中
78KB
721 行代码(不包括注释)
Lockjaw
Lockjaw 是一个完全静态的、编译时依赖注入框架,灵感来源于 依赖注入,专为 Rust 语言设计,并受 Dagger 启发。它也是被生锈的匕首刺伤时的感觉。
特性
- 编译时依赖解析
- Lockjaw 确保所有依赖在编译时都得到满足。如果缺少依赖、同一类型的绑定重复,或者依赖图有环,代码将无法编译。将不会出现难以检测的运行时错误。
- 相对 可读的诊断信息。
- 当缺少依赖时,Lockjaw 会尝试告诉你为什么它甚至在依赖图中,以及依赖循环在哪里。
- 跨crate注入
- Lockjaw 设计用于跨crate使用。客户端可以注入由库提供的绑定,如果它们也使用 Lockjaw。
- 最小的生成代码表面积
- 虽然 Lockjaw 严重利用了过程宏,但它避免了直接修改属性宏放置的代码。只有少数生成方法对用户可见。这对于大多数 Rust IDE 目前都不理解过程宏的输出来说尤其重要,一些额外的类型提示就足以使自动补全功能正常工作。
- 可选绑定、多绑定和为插件系统生成的组件
- Lockjaw 允许库 crate 和其用户之间的控制反转。库能够为依赖库的客户定义钩子以进行注入。这对于使用库独立测试多个客户端特别有用。
请参阅 用户指南 获取更多信息。
示例
use lockjaw::*;
use std::ops::Add;
lockjaw::prologue!("src/lib.rs");
struct GreetCounter {
counter: ::std::cell::RefCell<i32>
}
// Allow GreetCounter to be created in the dependency graph. These bindings are available anywhere.
#[injectable]
impl GreetCounter {
// Marks a method as the inject constructor. Lockjaw will call this to create the object.
#[inject]
pub fn new() -> Self {
Self{counter : std::cell::RefCell::new(0) }
}
}
impl GreetCounter{
pub fn increment(&self) -> i32 {
let mut m = self.counter.borrow_mut();
*m = m.add(1);
m.clone()
}
}
pub trait Greeter {
fn greet(&self) -> String;
}
struct GreeterImpl {
greet_counter : crate::GreetCounter,
phrase : String
}
#[injectable]
impl GreeterImpl {
// Lockjaw will call this with other injectable objects provided.
#[inject]
pub fn new(greet_counter : GreetCounter, phrase : String) -> Self {
Self {
greet_counter,
phrase
}
}
}
impl Greeter for GreeterImpl{
fn greet(&self) -> String{
format!("{} {}", self.phrase, self.greet_counter.increment())
}
}
// Declare a module so we can do special bindings. These bindings are only available if the
// component installs the module, so different bindings can be used based on the situation.
struct MyModule {}
#[module]
impl MyModule {
// When ever someone needs a Greeter, use GreeterImpl as the actual implementation
#[binds]
pub fn bind_greeter(_impl : crate::GreeterImpl) -> Cl<dyn Greeter> {}
// Called when a String is requested
#[provides]
pub fn provide_string() -> String {
"helloworld".to_owned()
}
}
// Components stitch modules and injectables together into a dependency graph, and can create
// objects in the graph. The component installs modules listed in `modules`
#[component(modules: MyModule)]
trait MyComponent {
// Allows creating a greeter with the component. The created object has the lifetime of the
// component
fn greeter(&self) -> Cl<dyn Greeter>;
}
pub fn main() {
// Creates the component
let component = MyComponent::new();
// Creates a greeter.
let greeter = component.greeter();
assert_eq!(greeter.greet(), "helloworld 1");
// Internal states of the greeter is kept.
assert_eq!(greeter.greet(), "helloworld 2");
// A new greeter has a new independent set of injected objects.
assert_eq!(component.greeter().greet(), "helloworld 1");
}
// called after the last use of lockjaw to perform validation and code generation
epilogue!();
更复杂的游戏示例可以在 https://github.com/azureblaze/lockjaw/tree/main/example_game 找到
与Dagger的比较
Lockjaw 致力于与 Dagger 具有功能一致性,并使用非常相似的 API。如果你以前使用过 Dagger,Lockjaw 应该感觉熟悉。
@Inject
→#[inject]
构造函数注入在#[injectable]
@Provides
→#[provides]
绑定方法返回值@Binds
→#[binds]
将绑定特质绑定到实现。@Singleton
/@Scope
→scope=component
共享实例。@Named
→#[qualified]
Provider<T>
→Provider<T>
在运行时创建多个实例。Lazy<T>
→Lazy<T>
只有在使用时才创建和缓存实例。- 子组件 →
#[subcomponent]
动态创建具有附加绑定的子作用域 - 多绑定 →
#[into_vec]
/#[into_map]
将相同的绑定收集到 Vec/HashMap 中,对于插件系统非常有用。 @BindsOptionalOf
→#[binds_option_of]
允许某些绑定缺失- 工厂 →
#[facotry]
创建具有注入字段和运行时字段的对象。 - Hilt →
#[define_component]
/#[entry_point
/install_in
从构建依赖中自动收集模块。
免责声明
这不是一个官方支持的产品。
Lockjaw 目前处于早期开发阶段,所有 API 都可能发生变化。一些功能也以一种巧妙的方式实现。使用风险自担。
依赖项
~16MB
~392K SLoC