15次发布
0.3.6 | 2024年1月31日 |
---|---|
0.3.5 | 2024年1月31日 |
0.3.3 | 2023年11月2日 |
0.3.1 | 2023年9月15日 |
0.1.4 | 2023年6月21日 |
#5 在 #async-trait 中
每月 76 次下载
45KB
790 行
Fama
Fama是一个管道管理器
Fama的意思是“传给某人”或“传给我”
Fama使得布置完成任务所需的步骤变得简单。过程中的每个步骤都称为一个“管道”。数据或内容通过“管道”传递。在任何阶段都可以停止流程。
这种模式通常被称为“管道”模式。它与中间件模式非常相似。
这种实现消除了当前“管道”在中间件模式中调用下一个管道的责任。一个“管道”可以应用其更改/逻辑或停止流程。是“管道”启动下一个调用。
以下示例展示了“新用户”流程通过管道。
#[tokio::main]
async fn main() {
// A new user instance is being passed through the pipeline
let new_user = fama::Pipeline::pass(NewUser::default()) // The input/content
.through(ValidateUserName).await // pipe "ValidateUserName" will stop the flow if the user does not have a "username"
.through(GenerateUserId).await // pipe "GenerateUserId" generates and set the user ID.
.through(ApplyDefaultRole).await // pipe "ApplyDefaultRole" will give the user the "Basic" role if the list of roles is empty
.through(SaveNewUserData).await // pipe "SaveNewUserData" saves the data to the database. At this stage, we know all is well
.through_fn(|c: fama::PipeContent| async {
println!("yep, you can pass a closure or function too");
Some(c)
}).await
.deliver(); // starts the process or use
// .confirm() // Return true when the content passes throug all the pipes
// Fails because "new user" does not have a "username"
println!("fails validation: {:#?}", &new_user);
println!("----------------------------------------");
let new_user2 = fama::Pipeline::pass(NewUser {
username: Some("user1".into()), // "new user" has a username
..NewUser::default()
})
.through(ValidateUserName).await
.through(GenerateUserId).await
.through(ApplyDefaultRole).await
.through(SaveNewUserData).await
.deliver();
println!(
"passes validation and all fields are set : {:#?}",
&new_user2
);
}
// The content for the pipeline input. Can be any type
#[derive(Debug, Clone)]
struct NewUser {
internal_id: i32,
id: Option<String>,
username: Option<String>,
role: Option<Vec<UserRole>>,
}
impl Default for NewUser {
fn default() -> Self {
Self {
internal_id: 0,
id: None,
username: None,
role: None,
}
}
}
// making the pipe input type injectable
#[fama::async_trait]
impl busybody::Injectable for NewUser {
async fn inject(c: &busybody::ServiceContainer) -> Self {
// get the instance of the type in the current scope or
// create a new instance
c.get_type().unwrap_or_else(|| Self::default())
}
}
// The various roles a user can have
#[derive(Debug, Clone)]
enum UserRole {
Admin,
ContentCreator,
Moderator,
Basic,
}
struct ValidateUserName;
// A struct becomes a pipe when it implements "fama::FamaPipe"
#[fama::async_trait]
impl fama::FamaPipe<(NewUser, fama::PipeContent), Option<fama::PipeContent>> for ValidateUserName {
// The only requirement is to implement "receive_pipe_content" method
async fn receive_pipe_content(&self, (new_user, mut content): (NewUser, fama::PipeContent)) -> Option<fama::PipeContent> {
//1 // When the username is "none", stop the flow
if new_user.username.is_none() {
println!("User name cannot be empty");
content.stop_the_flow(); // Stop the pipeline flow. Pipes below this pipe will not get call
}
Some(content)
}
}
struct GenerateUserId;
#[fama::async_trait]
impl fama::FamaPipe<(NewUser, fama::PipeContent), ()> for GenerateUserId {
async fn receive_pipe_content(&self, (mut new_user, content): (NewUser,fama::PipeContent)) {
if new_user.id.is_none() {
new_user.id = Some(uuid::Uuid::new_v4().to_string()); // Generate and set the ID
content.store(new_user);
}
}
}
struct ApplyDefaultRole;
#[fama::async_trait]
impl fama::FamaPipe<(NewUser, fama::PipeContent), Option<fama::PipeContent>> for ApplyDefaultRole {
async fn receive_pipe_content(&self, (mut new_user, content): (NewUser,fama::PipeContent)) -> Option<fama::PipeContent> {
if new_user.role.is_none() {
new_user.role = Some(vec![UserRole::Basic]); // Apply default role
content.store(new_user);
}
Some(content)
}
}
struct SaveNewUserData;
#[fama::async_trait]
impl fama::FamaPipe<(NewUser, fama::PipeContent), Option<fama::PipeContent>> for SaveNewUserData {
async fn receive_pipe_content(&self, (mut new_user, content): (NewUser,fama::PipeContent)) -> Option<fama::PipeContent> {
println!(">> saving new user: {:?}", &new_user);
new_user.internal_id = 1; // pretend we persisted the data to a database
content.store(new_user);
Some(content)
}
}
示例
示例文件夹 examples 中包含了一些示例。如果示例都不适用,请提出您的用例,我会尝试提供一个。
反馈
如果您觉得这个crate很有用,请给仓库点星。提交您的问题和建议。
许可证
MIT许可证 (MIT)
在此特此授予任何获得此软件及其相关文档副本(“软件”)的人免费处理该软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,并允许向软件提供的人这样做,但受以下条件约束
上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。
软件按“原样”提供,不提供任何形式的保证,明示或暗示,包括但不限于适销性、针对特定目的的适用性和非侵权性保证。在任何情况下,作者或版权所有者不对任何索赔、损害或其他责任负责,无论是基于合同、侵权或其他原因,由软件或其使用或其他交易引起。
依赖项
~3–4.5MB
~79K SLoC