6 روانش
0.3.0 | 19 جولای 2022 |
---|---|
0.2.1 | 27 آوریل 2022 |
0.1.2 | 4 آوریل 2022 |
0.1.1 | 21 مارس 2022 |
0.1.0 | 5 فوریه 2022 |
#240 در همزمان
9,330 بار دانلود در ماه
در 44 کیت (3 مستقیم) استفاده شده
76KB
1.5K SLoC
دپتری
اجراپذیری الگوی زنجیره (درخت) مسئولیت.
use dptree::prelude::*;
type WebHandler = Endpoint<'static, DependencyMap, String>;
#[rustfmt::skip]
#[tokio::main]
async fn main() {
let web_server = dptree::entry()
.branch(smiles_handler())
.branch(sqrt_handler())
.branch(not_found_handler());
assert_eq!(
web_server.dispatch(dptree::deps!["/smile"]).await,
ControlFlow::Break("🙃".to_owned())
);
assert_eq!(
web_server.dispatch(dptree::deps!["/sqrt 16"]).await,
ControlFlow::Break("4".to_owned())
);
assert_eq!(
web_server.dispatch(dptree::deps!["/lol"]).await,
ControlFlow::Break("404 Not Found".to_owned())
);
}
fn smiles_handler() -> WebHandler {
dptree::filter(|req: &'static str| req.starts_with("/smile"))
.endpoint(|| async { "🙃".to_owned() })
}
fn sqrt_handler() -> WebHandler {
dptree::filter_map(|req: &'static str| {
if req.starts_with("/sqrt") {
let (_, n) = req.split_once(' ')?;
n.parse::<f64>().ok()
} else {
None
}
})
.endpoint(|n: f64| async move { format!("{}", n.sqrt()) })
}
fn not_found_handler() -> WebHandler {
dptree::endpoint(|| async { "404 Not Found".to_owned() })
}
ویژگیها
- ✔️ مدیریتکنندگان تعریف شده:
dptree::{endpoint, filter, filter_map, ...}
. - ✔️ طراحی سبک کموزنی به صورت سبک انتقال به ادامه داخلی.
- ✔️ تزریق وابستگی از پیش.
- ✔️ پشتیبانی از عملیات زنجیرهای و شاخهای مدیریتکنندگان.
- ✔️ امکان بررسی مدیریتکنندگان.
- ✔️ آزمایش شده در جنگ: دپتری در teloxide به عنوان یک چارچوب برای مدیریت ارسال بهروزرسانیهای تلگرام استفاده میشود.
- ✔️ بیتفاوتی به زمان اجرا: از فقط کیت futures استفاده میکند.
توضیح
کد بالا یک درخت مدیریت وبسرور ساده است. در کد به صورت توصیفی، به این صورت خواهد بود
dptree::entry()
: ارسال یک بهروزرسانی به مدیریتکنندگان شاخه زیر.branch(smiles_handler())
: اگر بهروزرسانی شرایط را رعایت کند (dptree::filter
)، یک لبخند بازگردانید (.endpoint
). در غیر این صورت، بهروزرسانی را به سمت جلو ارسال کنید..branch(sqrt_handler())
:如果更新是数字(dptree::filter_map
),则返回它的平方。否则,传递更新。.branch(not_found_handler())
:立即返回404 Not Found
。
控制流:如您所见,我们刚刚描述了一个由三个分支组成的调度方案。首先,dptree 进入第一个处理程序 smiles_handler
,然后,如果它无法处理更新,则将更新传递给 sqrt_handler
,依此类推。如果没有人成功处理更新,则控制流进入 not_found_handler
并返回错误。换句话说,整个 .dispatch
调用的结果是第一个成功处理传入更新的处理程序的结果。
依赖注入:我们不是直接将值传递给 .dispatch
,而是使用 dptree::deps!
宏。它接受一系列值,并从中构建 DependencyMap
。处理程序在其签名中请求特定类型的值(例如 |req: &'static str|
),dptree 自动 注入 来自 dptree::deps!
的值到这些函数中。如果它无法获取请求类型的值,它将在运行时 panic,所以请小心,并在将代码推送到生产环境之前始终测试您的代码。
使用 dptree,您可以使用您在上面看到的相同重复模式指定任意复杂的调度方案。
陷阱
DependencyMap
如果请求一个不存在的依赖项,则会在运行时 panic。始终测试您的代码,并确保在请求之前所有依赖项都已指定 。.branch
和.chain
是不同的操作。请参阅 "链式和分支之间的区别"。
设计选择
函数式
我们决定在内部使用 延续传递风格 (CPS) 并向库用户公开整洁的处理程序模式。这与您在典型的责任链模式面向对象模型中看到的情况相反。事实上,我们首先尝试制作典型的OO设计,但后来因为其简洁性而转向FP。使用此设计,每个处理程序都接受一个表示链中剩余处理程序的延续;处理程序可以调用这个延续也可以不调用。使用这个简单的模型,我们可以使用仅使用函数而无需其他任何东西来表示几乎任何处理程序模式,如 filter
和 filter_map
。您不需要抽象工厂、构建器等复杂的编程机制。
DI
在Rust中,可以使用类型安全的依赖注入容器来替代在运行时引发恐慌的DependencyMap
。然而,这需要复杂的类型级别操作(类似于frunk库中的操作)。我(@Hirrolot)和@p0lunin决定不为了编译时安全性而牺牲可理解的错误信息,因为我们有很多经验表明,新手用户根本无法理解他们的代码出了什么问题,因为rustc的诊断信息极其不充分。
故障排除
的特质 约束[闭包@examples/state_machine.rs:150:20: 150:92]: Injectable<_, bool, _> 不满足
这个错误表示您的处理器没有实现Injectable
特质。请确保您的更新类型实现了Clone
。如果对每个更新进行克隆太昂贵,您可以将其包装在Arc
中。
依赖项
~1MB
~16K SLoC