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

Download history 76/week @ 2024-07-24

每月 76 次下载

MIT 许可证

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