dataflow_rs/lib.rs
1/*!
2# Dataflow-rs
3
4A lightweight, rule-driven workflow engine for building powerful data processing pipelines and nanoservices in Rust.
5
6## Overview
7
8Dataflow-rs provides a flexible and extensible framework for processing data through a series of tasks organized in workflows.
9The engine automatically routes messages through appropriate workflows based on configurable rules, and each workflow can
10contain multiple tasks that transform, validate, or enrich the data.
11
12## Key Components
13
14* **Engine**: The central component that processes messages through workflows
15* **Workflow**: A collection of tasks with conditions that determine when they should be applied
16* **Task**: An individual processing unit that performs a specific function on a message
17* **AsyncFunctionHandler**: A trait implemented by task handlers to define custom async processing logic
18* **Message**: The data structure that flows through the engine, containing payload, metadata, and processing results
19
20## Built-in Functions
21
22The engine comes with several pre-registered functions:
23
24* **map**: Maps and transforms data between different parts of a message
25* **validate**: Validates message data against rules using JSONLogic expressions
26
27## Usage Example
28
29```rust,no_run
30use dataflow_rs::{Engine, Workflow};
31use dataflow_rs::engine::message::Message;
32use serde_json::json;
33
34#[tokio::main]
35async fn main() -> Result<(), Box<dyn std::error::Error>> {
36 // Define a workflow in JSON
37 let workflow_json = r#"
38 {
39 "id": "data_processor",
40 "name": "Data Processor",
41 "priority": 0,
42 "tasks": [
43 {
44 "id": "transform_data",
45 "name": "Transform Data",
46 "function": {
47 "name": "map",
48 "input": {
49 "mappings": [
50 {
51 "path": "data.result",
52 "logic": { "var": "temp_data.value" }
53 }
54 ]
55 }
56 }
57 }
58 ]
59 }
60 "#;
61
62 // Parse the workflow
63 let workflow = Workflow::from_json(workflow_json)?;
64
65 // Create the workflow engine with the workflow (built-in functions are auto-registered by default)
66 let engine = Engine::new(vec![workflow], None);
67
68 // Create a message to process
69 let mut message = Message::from_value(&json!({}));
70
71 // Process the message through the workflow
72 match engine.process_message(&mut message).await {
73 Ok(_) => {
74 println!("Processed result: {}", message.context["data"]["result"]);
75 }
76 Err(e) => {
77 println!("Error in workflow: {:?}", e);
78 }
79 }
80
81 Ok(())
82}
83```
84
85## Error Handling
86
87The library provides a comprehensive error handling system:
88
89```rust,no_run
90use dataflow_rs::{Engine, Result, DataflowError};
91use dataflow_rs::engine::message::Message;
92use serde_json::json;
93
94#[tokio::main]
95async fn main() -> Result<()> {
96 // ... setup workflows ...
97 let engine = Engine::new(vec![/* workflows */], None);
98
99 let mut message = Message::from_value(&json!({}));
100
101 // Process the message, errors will be collected but not halt execution
102 engine.process_message(&mut message).await?;
103
104 // Check if there were any errors during processing
105 if message.has_errors() {
106 for error in &message.errors {
107 println!("Error in workflow: {:?}, task: {:?}: {:?}",
108 error.workflow_id, error.task_id, error.message);
109 }
110 }
111
112 Ok(())
113}
114```
115
116## Extending with Custom Functions
117
118You can extend the engine with your own custom function handlers:
119
120```rust,no_run
121use dataflow_rs::{Engine, AsyncFunctionHandler, Result, Workflow};
122use dataflow_rs::engine::{FunctionConfig, message::{Change, Message}, error::DataflowError};
123use datalogic_rs::DataLogic;
124use serde_json::{json, Value};
125use std::collections::HashMap;
126use std::sync::Arc;
127use async_trait::async_trait;
128
129struct CustomFunction;
130
131#[async_trait]
132impl AsyncFunctionHandler for CustomFunction {
133 async fn execute(
134 &self,
135 message: &mut Message,
136 config: &FunctionConfig,
137 datalogic: Arc<DataLogic>,
138 ) -> Result<(usize, Vec<Change>)> {
139 // Implement your custom logic here
140
141 // Extract the custom configuration from config
142 let input = match config {
143 FunctionConfig::Custom { input, .. } => input,
144 _ => return Err(DataflowError::Validation("Invalid configuration type".to_string())),
145 };
146
147 // Validate input
148 let required_field = input.get("field")
149 .ok_or_else(|| DataflowError::Validation("Missing required field".to_string()))?
150 .as_str()
151 .ok_or_else(|| DataflowError::Validation("Field must be a string".to_string()))?;
152
153 // Record changes for audit trail
154 let changes = vec![
155 Change {
156 path: Arc::from("data.custom_field"),
157 old_value: Arc::new(Value::Null),
158 new_value: Arc::new(json!("custom value")),
159 }
160 ];
161
162 // Return success code (200) and changes
163 Ok((200, changes))
164 }
165}
166
167#[tokio::main]
168async fn main() -> Result<()> {
169 // Create custom functions
170 let mut custom_functions = HashMap::new();
171 custom_functions.insert(
172 "custom".to_string(),
173 Box::new(CustomFunction) as Box<dyn AsyncFunctionHandler + Send + Sync>
174 );
175
176 // Create engine with workflows and custom functions
177 let engine = Engine::new(vec![/* workflows */], Some(custom_functions));
178
179 // Now it can be used in workflows...
180 Ok(())
181}
182```
183*/
184
185pub mod engine;
186
187// Re-export all public APIs for easier access
188pub use engine::error::{DataflowError, ErrorInfo, Result};
189pub use engine::functions::{
190 AsyncFunctionHandler, FunctionConfig, MapConfig, MapMapping, ValidationConfig, ValidationRule,
191};
192pub use engine::message::{AuditTrail, Change, Message};
193pub use engine::{Engine, Task, Workflow};