(Oct. 3rd 2024) Flawless is now in Public Beta. Try it out!
  Flawless Logo, a beautiful woman with freckles head illustration. flawless.dev docs discord

Integration with Rust projects

Flawless workflows are compiled into WebAssembly modules and executed separately from your Rust native code. To simplify the communication between your native code and the workflows, you can use the flawless-utils crate.

This is especially useful when it comes to development, where you will be rapidly iterating, on both your native code and workflows. flawless-utils lets you automatically build the WebAssembly code and redeploy on every change, reducing the feedback loop.

Folder structure

flawless-utils expects a specific folder structure. It expects all the crates containing Flawless modules to be located under a workflows folder in your Rust project. Your folder structure should look similar to this:

root
- src/main.rs
- workflows/
    - module1/
        - src/lib.rs
        - Cargo.toml
- build.rs
- Cargo.toml

Automatic recompilation

To set up automatic recompilation you will need to add a build.rs file to your Rust project.

// build.rs

fn main() {
    // Re-run build if workflow changed.
    println!("cargo:rerun-if-changed=workflows");
    // Builds the project located in `workflows/module1`
    // into a Flawless WebAssembly artifact.
    flawless_utils::build("module1");
}

Now every time you change your workflow a new WebAssembly module is going to be built together with the compilation of the rest of your project.

Automatic deployment

It's also a good idea to deploy the latest module together with your application on startup. I like to do this right when the application starts up in the main.rs file.

// src/main.rs

#[tokio::main]
async fn main() {
    let flawless
        = flawless_utils::Server::new("http://localhost:27288", None);
    let flawless_module
        = flawless_utils::load_module_from_build!("module1");
    let module
        = flawless.deploy(flawless_module).await.unwrap();
}

The load_module_from_build! macro will reference the module that was built inside the build.rs script.

Interacting with the module

Once you have a module deployed, you can start workflows from it. It's best to use the Flawless module crate as a dependency in your Rust project.

The following example workflow:

// workflows/module1/src/lib.rs

use flawless::{workflow, workflow::Input};

flawless::module! { name = "module1", version = "0.0.1" }

#[workflow("start_job")]
pub fn start_job(input: Input<Job>) {
    // ... workflow code
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Job {
    // ... fields belonging to the job
}

Can be started using the following code:

// src/main.rs
use module1::{start_job, Job};

#[tokio::main]
async fn main() {
    let flawless
        = flawless_utils::Server::new("http://localhost:27288", None);
    let flawless_module
        = flawless_utils::load_module_from_build!("module1");
    let module
        = flawless.deploy(flawless_module).await.unwrap();

    module.start::<start_job>(Job { /* fileds */ }).await.unwrap()
}

Notice that type safety of the input argument is directly enforced when the workflow is used as a dependency and not started explicitly by name using the module.start_by_name("start_job", Job { /* fileds */ }) method.

Next steps

Check also out workflow fundamentals, for examples on how to pass input values to workflows and how to get some output from them.