#![allow(dead_code)] use fama::PipeContent; #[tokio::main] async fn main() { // 1. Create a pipeline let new_user = fama::Pipeline::pass(NewUser { username: Some("awesomeuser".into()), ..NewUser::default() }) // Start of the pipeline .through(ValidateUserName) // pipe .await .through(GenerateUserId) // pipe .await .through(ApplyDefaultRole) // ... .await // Using a closure or a function .through_fn(|mut new_user: NewUser, pipe: PipeContent| async move { println!(">> saving new user: {:?}", &new_user); new_user.internal_id = 1; pipe.store(new_user); }) .await .deliver(); println!("fails validation: {:#?}", &new_user); // The flow is stopped by the "ValidateUserName" pipe because the user does not have a "username" } // pipeline input // Must be clonable. A clone of the data is passed to any pipe that requires it // The type must also be injectable. See futher below #[derive(Debug, Clone)] struct NewUser { internal_id: i32, id: Option, username: Option, role: Option>, } 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 { c.proxy_value().unwrap_or_else(|| Self::default()) } } #[derive(Debug, Clone)] enum UserRole { Admin, ContentCreator, Moderator, Basic, } struct ValidateUserName; // A pipe must implemenet the `FamaPipe' trait // In this case, ValidateUserName is expecting a tuple with two fields (NewUser, PipeContent) // A pipe can specify a parent parameter that is injectable. A tuple can be use when you // need to specifiy more arguements like in this case. #[fama::async_trait] impl fama::FamaPipe<(NewUser, PipeContent), Option> for ValidateUserName { async fn receive_pipe_content( &self, (new_user, content): (NewUser, PipeContent), ) -> Option { // When the username is "none", stop the flow if new_user.username.is_none() { println!("User name cannot be empty"); content.stop_the_flow(); // notify the pipeline to stop flowing. } Some(content) } } struct GenerateUserId; #[fama::async_trait] impl fama::FamaPipe<(NewUser, PipeContent), ()> for GenerateUserId { async fn receive_pipe_content(&self, (mut new_user, pipe): (NewUser, PipeContent)) { if new_user.id.is_none() { new_user.id = Some(uuid::Uuid::new_v4().to_string()); pipe.store(new_user); // Store the changes to the input } } } struct ApplyDefaultRole; #[fama::async_trait] impl fama::FamaPipe<(NewUser, PipeContent), ()> for ApplyDefaultRole { async fn receive_pipe_content(&self, (mut new_user, pipe): (NewUser, PipeContent)) { if new_user.role.is_none() { new_user.role = Some(vec![UserRole::Basic]); pipe.store(new_user); } } }