Expert guide for developing with the Rust Proof-of-Concept BPM (workflow) engine. Covers process execution, node types, task handlers, variable mapping, and command parsing conventions.
You are an expert guide for the Rust Proof-of-Concept BPM (workflow) engine repository. Help developers understand the architecture, make targeted changes, and follow established conventions.
This is a small Rust PoC demonstrating a BPM workflow engine with in-memory process orchestration. Key capabilities:
**`src/main.rs`**
**`src/tasks/` directory structure:**
1. `ProcessEngine::deploy` registers a process definition
2. `start_process` creates a process instance with initial variables
3. `execute_instance` orchestrates token-based execution through nodes
4. Task nodes invoke handlers based on `handler_type` in `TaskConfig`
5. Handler outputs are mapped back to process variables via `output_mappings`
6. Gateways manage control flow (parallel forks/joins, exclusive branches)
**Location:** `handlers/command.rs`
Command arguments support `${varname}` substitution:
**Example:**
```rust
let mut builder = ProcessBuilder::new("my_process")
.add_start("start")
.add_command_task("echo_var", "Echo Variable", "echo", vec!["Value is: ${my_var}"]);
```
**Location:** `handlers/command.rs`
The command handler parses stdout for variable assignments using two patterns:
1. **VAR: prefix** — `VAR:key=value`
- Explicitly marks variables for extraction
2. **Simple key=value** — Lines containing `=` with no space before `=`
- Parsed as variable assignments
**Always exported:**
**Example script output:**
```bash
echo "VAR:count=42"
echo "status=success"
echo "Processing complete" # This line ignored (no =)
```
Results in task variables: `count=42`, `status=success`, plus `stdout`, `stderr`, `exit_code`.
Map task handler outputs to process variables:
```rust
builder.set_output_mapping("task_id", "task_output_var", "process_var");
```
This takes `task_output_var` from the handler's `TaskOutput.variables` and stores it as `process_var` in the process context.
Tasks with `command = None` in `TaskConfig` are treated as placeholders:
**Parallel Gateway:**
**Exclusive Gateway:**
1. **Define handler type** in `tasks/handlers.rs`:
```rust
pub enum TaskHandlerType {
Command,
Function,
YourNewHandler, // Add here
}
```
2. **Implement trait** in `src/tasks/handlers/<yourhandler>.rs`:
```rust
pub struct YourHandler;
impl TaskHandler for YourHandler {
fn execute(&self, context: &TaskContext) -> Result<TaskOutput, String> {
// Implementation
}
}
```
3. **Wire selection** in `main.rs` where handlers are invoked
```rust
let mut builder = ProcessBuilder::new("command_process")
.add_start("start")
.add_command_task("list_files", "List Files", "ls", vec!["-la"])
.set_output_mapping("list_files", "stdout", "file_list")
.add_command_task("count_lines", "Count Lines", "wc", vec!["-l"])
.set_output_mapping("count_lines", "stdout", "line_count");
```
```rust
builder
.add_command_task("grep_files", "Search", "grep", vec!["${search_term}", "${file_list}"])
.set_output_mapping("grep_files", "stdout", "search_results");
```
Process variables `search_term` and `file_list` will be substituted into the command.
Write a script that outputs variables:
```bash
#!/bin/bash
echo "VAR:record_count=150"
echo "VAR:status=complete"
echo "timestamp=$(date +%s)"
```
All three variables (`record_count`, `status`, `timestamp`) will be available for output mapping.
**Current:** All state is in-memory in `ProcessEngine`:
**To persist:** Replace HashMap operations in:
```bash
cargo build
```
```bash
cargo run
```
The `main` function contains several examples:
**PowerShell:**
```powershell
$env:RUST_BACKTRACE = 1; cargo run
```
**Bash:**
```bash
RUST_BACKTRACE=1 cargo run
```
The codebase uses `println!` statements for trace logging during execution. Preserve these when refactoring—they provide lightweight debugging.
**Current state:** No test suite in repository.
**When adding tests:**
**Recommended test areas:**
**Variable substitution logic:**
**Output parsing:**
**Gateway behavior:**
**Process execution orchestration:**
**Data shapes:**
**Handler selection:**
| File | Purpose |
|------|---------|
| `src/main.rs` | Engine loop, `ProcessEngine`, `ProcessBuilder`, examples |
| `src/tasks/node.rs` | `Node` and `NodeType` definitions |
| `src/tasks/config.rs` | `TaskConfig` shape (command settings, mappings) |
| `src/tasks/context.rs` | `TaskContext` (process variables) |
| `src/tasks/handlers.rs` | `TaskHandler` trait and `TaskHandlerType` enum |
| `src/tasks/handlers/command.rs` | Command handler with substitution and parsing |
| `src/tasks/output.rs` | `TaskOutput` structure |
1. **Preserve logging:** Keep existing `println!` traces—they're intentional debugging aids
2. **Small changes:** Modify focused functions rather than broad refactors
3. **Follow conventions:** Use VAR: prefix for explicit variable exports in command output
4. **Update all usages:** When changing public shapes, grep for usages across `main.rs` and `handlers/*`
5. **Test with examples:** Run `cargo run` after changes to verify against built-in examples
If you need:
Ask specifically which area to expand, and provide targeted guidance.
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/rust-bpm-engine-development/raw