Execution
Crossflow is first and foremost a library to facilitate the execution of workflows, so there is not a single canonical way for JSON diagrams to be executed. However, there are certain requirements to meet and recommended code paths to follow for an application to be effective at executing diagrams.
Just like when workflows are built the native Rust API, a crossflow JSON diagram executor needs to be built as part of a Bevy App. You can either make a Bevy app that is entirely dedicated to executing JSON diagrams, or you can make the execution of JSON diagrams simply one feature within a broader app. In order for your app to execute diagrams, you will need to create a system that can receive JSON diagrams, build the diagrams into executable workflows, and then run those workflows.
The most important piece to understand when implementing an executor app is the DiagramElementRegistry.
The registry stores “builders” that allow the operations ("ops") within a JSON diagram to be translated into workflow elements that can actually be executed.
There are three types of registrations present in the registry:
- Message registration stores information about the message types supported by the executor, including what operations can be performed on the message type and how to perform it.
- Node builder registration stores the Node Builders that allow
"type": "node"operations in a diagram to be built into workflow nodes. This registration also stores the schema of the"config"field for each unique"builder"ID. - Section builder registration is similar to the node builder registration, except it stores information on Section Builders.
Creating a registry
Initializing a registry is simple:
use crossflow::prelude::*;
let mut registry = DiagramElementRegistry::new();
This will create a new registry that only contains registrations for the the “builtin” message types:
- Rust primitive types
StringJsonMessage
You’ll notice that we declared mut registry as mutable in the above code snippet.
This is because the registry isn’t very useful until you start registering your own node builders.
Without any node builders, your executor will only be able to build workflows that exclusively use the builtin message types listed above and the basic builtin operations.
Registering a node builder will allow you to build workflows that use custom services.
Use DiagramElementRegistry::register_node_builder as shown below to register a new node builder.
registry.register_node_builder(
NodeBuilderOptions::new("add"),
|builder, config: f32| {
builder.create_map_block(move |request: f32| {
request + config
})
}
);
Node builders are covered in more detail on the next page.
Tip
When you register a node builder, you will also automatically register any input and output messages types needed by the node builder.
Building workflows with a registry
Once you’ve registered all the builders that your executor needs, you can start building workflows from diagrams.
Simply create a valid Diagram instance and then call Diagram::spawn_io_workflow:
use serde_json::json;
let diagram_json = json!(
{
"version": "0.1.0",
"start": "add_5",
"ops": {
"add_5": {
"type": "node",
"builder": "add",
"config": 5,
"next": { "builtin": "terminate" }
}
}
}
);
let diagram = Diagram::from_json(diagram_json).unwrap();
let workflow = diagram.spawn_io_workflow(&mut commands, ®istry).unwrap();
Note
In the above example,
commandsis aCommandsinstance. Typically you will need to create a Bevy system that receives JSON diagrams and builds them into workflows.
Executing built workflows
Once you’ve used the Diagram and registry to build the workflow, you will be holding a Service that you can execute.
From there you can follow the same guidance in How to Run a Service or How to Run a Series.
Tip
To build the service and execute the workflow, your executor application will need to know the input and output message types of the diagram at compile time.
In most cases you can’t expect all incoming diagrams to have the exact same input and output message types as each other, so instead you can use
JsonMessageas both the input and output message types (RequestandResponse) of all diagrams.As long as the actual input and output message types of the diagrams are deserializable and serializable (respectively), the workflow builder can convert to/from
JsonMessageto run the workflow and receive its response.
Premade Executor
If you would like to get started with executing crossflow diagrams with minimal effort, you can use the crossflow-diagram-editor library to quickly make a basic executor.
The calculator example shows how to use the crossflow-diagram-editor to create a workflow executor that provides simple calculator operations.
For your own custom executor, you can replace the calculator node builders with your own more useful node builders.
use crossflow_diagram_editor::basic_executor::{self, DiagramElementRegistry, Error};
fn main() -> Result<(), Box<dyn Error>> {
// Create a new regsitry with the default message types pre-registered.
let mut registry = DiagramElementRegistry::new();
// Register calculator-inspired node builders from the calculator_ops_catalog library.
calculator_ops_catalog::register(&mut registry);
// Run the basic executor
basic_executor::run(registry)
}