move rhailib to herolib

This commit is contained in:
Timur Gordon
2025-08-21 14:32:24 +02:00
parent aab2b6f128
commit aa0248ef17
121 changed files with 16412 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
use heromodels::db::Db;
use macros::{
register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn,
register_authorized_get_by_id_fn,
};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::flow::flow::Flow;
use heromodels::models::flow::flow_step::FlowStep;
type RhaiFlow = Flow;
type RhaiFlowStep = FlowStep;
#[export_module]
mod rhai_flow_module {
use super::{Array, Dynamic, RhaiFlow, RhaiFlowStep, INT};
#[rhai_fn(name = "new_flow", return_raw)]
pub fn new_flow() -> Result<RhaiFlow, Box<EvalAltResult>> {
Ok(Flow::new())
}
// --- Setters ---
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(flow: &mut RhaiFlow, name: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned = std::mem::take(flow);
*flow = owned.name(name);
Ok(flow.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(flow: &mut RhaiFlow, status: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned = std::mem::take(flow);
*flow = owned.status(status);
Ok(flow.clone())
}
#[rhai_fn(name = "add_step", return_raw)]
pub fn add_step(
flow: &mut RhaiFlow,
step: RhaiFlowStep,
) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned = std::mem::take(flow);
*flow = owned.add_step(step);
Ok(flow.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)]
pub fn get_id(f: &mut RhaiFlow) -> INT {
f.base_data.id as INT
}
#[rhai_fn(get = "name", pure)]
pub fn get_name(f: &mut RhaiFlow) -> String {
f.name.clone()
}
#[rhai_fn(get = "status", pure)]
pub fn get_status(f: &mut RhaiFlow) -> String {
f.status.clone()
}
#[rhai_fn(get = "steps", pure)]
pub fn get_steps(f: &mut RhaiFlow) -> Array {
f.steps.clone().into_iter().map(Dynamic::from).collect()
}
}
pub fn register_flow_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiFlow>();
let mut module = exported_module!(rhai_flow_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_flow",
resource_type_str: "Flow",
rhai_return_rust_type: heromodels::models::flow::flow::Flow
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_flow",
resource_type_str: "Flow",
rhai_return_rust_type: heromodels::models::flow::flow::Flow
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_flow",
resource_type_str: "Flow",
rhai_return_rust_type: heromodels::models::flow::flow::Flow
);
engine.register_global_module(module.into());
}

View File

@@ -0,0 +1,86 @@
use heromodels::db::Db;
use macros::{
register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn,
register_authorized_get_by_id_fn,
};
use rhai::plugin::*;
use rhai::{Dynamic, Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::flow::flow_step::FlowStep;
type RhaiFlowStep = FlowStep;
#[export_module]
mod rhai_flow_step_module {
use super::{RhaiFlowStep, INT};
#[rhai_fn(name = "new_flow_step", return_raw)]
pub fn new_flow_step() -> Result<RhaiFlowStep, Box<EvalAltResult>> {
Ok(FlowStep::default())
}
// --- Setters ---
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(
step: &mut RhaiFlowStep,
description: String,
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
let owned = std::mem::take(step);
*step = owned.description(description);
Ok(step.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(
step: &mut RhaiFlowStep,
status: String,
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
let owned = std::mem::take(step);
*step = owned.status(status);
Ok(step.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)]
pub fn get_id(s: &mut RhaiFlowStep) -> INT {
s.base_data.id as INT
}
#[rhai_fn(get = "description", pure)]
pub fn get_description(s: &mut RhaiFlowStep) -> Option<String> {
s.description.clone()
}
#[rhai_fn(get = "status", pure)]
pub fn get_status(s: &mut RhaiFlowStep) -> String {
s.status.clone()
}
}
pub fn register_flow_step_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiFlowStep>();
let mut module = exported_module!(rhai_flow_step_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_flow_step",
resource_type_str: "FlowStep",
rhai_return_rust_type: heromodels::models::flow::flow_step::FlowStep
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_flow_step",
resource_type_str: "FlowStep",
rhai_return_rust_type: heromodels::models::flow::flow_step::FlowStep
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_flow_step",
resource_type_str: "FlowStep",
rhai_return_rust_type: heromodels::models::flow::flow_step::FlowStep
);
engine.register_global_module(module.into());
}

View File

@@ -0,0 +1,17 @@
use rhai::Engine;
pub mod flow;
pub mod flow_step;
pub mod signature_requirement;
pub mod orchestrated_flow;
pub mod orchestrated_flow_step;
// Re-export the orchestrated models for easy access
pub use orchestrated_flow::{OrchestratedFlow, OrchestratorError, FlowStatus};
pub use orchestrated_flow_step::OrchestratedFlowStep;
pub fn register_flow_rhai_modules(engine: &mut Engine) {
flow::register_flow_rhai_module(engine);
flow_step::register_flow_step_rhai_module(engine);
signature_requirement::register_signature_requirement_rhai_module(engine);
}

View File

@@ -0,0 +1,154 @@
//! Orchestrated Flow model for DAG-based workflow execution
use heromodels_core::BaseModelData;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use thiserror::Error;
use super::orchestrated_flow_step::OrchestratedFlowStep;
/// Extended Flow with orchestrator-specific steps
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrchestratedFlow {
/// Base model data (id, created_at, updated_at)
pub base_data: BaseModelData,
/// Name of the flow
pub name: String,
/// Orchestrated steps with dependencies
pub orchestrated_steps: Vec<OrchestratedFlowStep>,
}
impl OrchestratedFlow {
/// Create a new orchestrated flow
pub fn new(name: &str) -> Self {
Self {
base_data: BaseModelData::new(),
name: name.to_string(),
orchestrated_steps: Vec::new(),
}
}
/// Add a step to the flow
pub fn add_step(mut self, step: OrchestratedFlowStep) -> Self {
self.orchestrated_steps.push(step);
self
}
/// Get the flow ID
pub fn id(&self) -> u32 {
self.base_data.id
}
/// Validate the DAG structure (no cycles)
pub fn validate_dag(&self) -> Result<(), OrchestratorError> {
let mut visited = HashSet::new();
let mut rec_stack = HashSet::new();
for step in &self.orchestrated_steps {
if !visited.contains(&step.id()) {
if self.has_cycle(step.id(), &mut visited, &mut rec_stack)? {
return Err(OrchestratorError::CyclicDependency);
}
}
}
Ok(())
}
/// Check for cycles in the dependency graph
fn has_cycle(
&self,
step_id: u32,
visited: &mut HashSet<u32>,
rec_stack: &mut HashSet<u32>,
) -> Result<bool, OrchestratorError> {
visited.insert(step_id);
rec_stack.insert(step_id);
let step = self.orchestrated_steps
.iter()
.find(|s| s.id() == step_id)
.ok_or(OrchestratorError::StepNotFound(step_id))?;
for &dep_id in &step.depends_on {
if !visited.contains(&dep_id) {
if self.has_cycle(dep_id, visited, rec_stack)? {
return Ok(true);
}
} else if rec_stack.contains(&dep_id) {
return Ok(true);
}
}
rec_stack.remove(&step_id);
Ok(false)
}
}
/// Orchestrator errors
#[derive(Error, Debug)]
pub enum OrchestratorError {
#[error("Database error: {0}")]
DatabaseError(String),
#[error("Executor error: {0}")]
ExecutorError(String),
#[error("No ready steps found - possible deadlock")]
NoReadySteps,
#[error("Step {0} failed: {1:?}")]
StepFailed(u32, Option<String>),
#[error("Cyclic dependency detected in workflow")]
CyclicDependency,
#[error("Step {0} not found")]
StepNotFound(u32),
#[error("Invalid dependency: step {0} depends on non-existent step {1}")]
InvalidDependency(u32, u32),
}
/// Flow execution status
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum FlowStatus {
Pending,
Running,
Completed,
Failed,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_orchestrated_flow_builder() {
let step1 = OrchestratedFlowStep::new("step1").script("let x = 1;");
let step2 = OrchestratedFlowStep::new("step2").script("let y = 2;");
let flow = OrchestratedFlow::new("test_flow")
.add_step(step1)
.add_step(step2);
assert_eq!(flow.name, "test_flow");
assert_eq!(flow.orchestrated_steps.len(), 2);
}
#[test]
fn test_dag_validation_no_cycle() {
let step1 = OrchestratedFlowStep::new("step1").script("let x = 1;");
let step2 = OrchestratedFlowStep::new("step2")
.script("let y = 2;")
.depends_on(step1.id());
let flow = OrchestratedFlow::new("test_flow")
.add_step(step1)
.add_step(step2);
assert!(flow.validate_dag().is_ok());
}
}

View File

@@ -0,0 +1,124 @@
//! Orchestrated Flow Step model for DAG-based workflow execution
use heromodels_core::BaseModelData;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Extended FlowStep with orchestrator-specific fields
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrchestratedFlowStep {
/// Base model data (id, created_at, updated_at)
pub base_data: BaseModelData,
/// Name of the flow step
pub name: String,
/// Rhai script to execute
pub script: String,
/// IDs of steps this step depends on
pub depends_on: Vec<u32>,
/// Execution context (circle)
pub context_id: String,
/// Target worker for execution
pub worker_id: String,
/// Input parameters
pub inputs: HashMap<String, String>,
/// Output results
pub outputs: HashMap<String, String>,
}
impl OrchestratedFlowStep {
/// Create a new orchestrated flow step
pub fn new(name: &str) -> Self {
Self {
base_data: BaseModelData::new(),
name: name.to_string(),
script: String::new(),
depends_on: Vec::new(),
context_id: String::new(),
worker_id: String::new(),
inputs: HashMap::new(),
outputs: HashMap::new(),
}
}
/// Set the script content
pub fn script(mut self, script: &str) -> Self {
self.script = script.to_string();
self
}
/// Add a dependency on another step
pub fn depends_on(mut self, step_id: u32) -> Self {
self.depends_on.push(step_id);
self
}
/// Set the context ID
pub fn context_id(mut self, context_id: &str) -> Self {
self.context_id = context_id.to_string();
self
}
/// Set the worker ID
pub fn worker_id(mut self, worker_id: &str) -> Self {
self.worker_id = worker_id.to_string();
self
}
/// Add an input parameter
pub fn input(mut self, key: &str, value: &str) -> Self {
self.inputs.insert(key.to_string(), value.to_string());
self
}
/// Get the step ID
pub fn id(&self) -> u32 {
self.base_data.id
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_orchestrated_flow_step_builder() {
let step = OrchestratedFlowStep::new("test_step")
.script("let x = 1;")
.context_id("test_context")
.worker_id("test_worker")
.input("key1", "value1");
assert_eq!(step.name, "test_step");
assert_eq!(step.script, "let x = 1;");
assert_eq!(step.context_id, "test_context");
assert_eq!(step.worker_id, "test_worker");
assert_eq!(step.inputs.get("key1"), Some(&"value1".to_string()));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_orchestrated_flow_step_builder() {
let step = OrchestratedFlowStep::new("test_step")
.script("let x = 1;")
.context_id("test_context")
.worker_id("test_worker")
.input("key1", "value1");
assert_eq!(step.flow_step.name, "test_step");
assert_eq!(step.script, "let x = 1;");
assert_eq!(step.context_id, "test_context");
assert_eq!(step.worker_id, "test_worker");
assert_eq!(step.inputs.get("key1"), Some(&"value1".to_string()));
}
}

View File

@@ -0,0 +1,145 @@
use heromodels::db::Db;
use macros::{
register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn,
register_authorized_get_by_id_fn,
};
use rhai::plugin::*;
use rhai::{Dynamic, Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::flow::signature_requirement::SignatureRequirement;
type RhaiSignatureRequirement = SignatureRequirement;
#[export_module]
mod rhai_signature_requirement_module {
use super::{RhaiSignatureRequirement, INT};
#[rhai_fn(name = "new_signature_requirement", return_raw)]
pub fn new_signature_requirement() -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
Ok(SignatureRequirement::default())
}
// --- Setters ---
#[rhai_fn(name = "flow_step_id", return_raw)]
pub fn set_flow_step_id(
sr: &mut RhaiSignatureRequirement,
flow_step_id: INT,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let mut owned = std::mem::take(sr);
owned.flow_step_id = flow_step_id as u32;
*sr = owned;
Ok(sr.clone())
}
#[rhai_fn(name = "public_key", return_raw)]
pub fn set_public_key(
sr: &mut RhaiSignatureRequirement,
public_key: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let mut owned = std::mem::take(sr);
owned.public_key = public_key;
*sr = owned;
Ok(sr.clone())
}
#[rhai_fn(name = "message", return_raw)]
pub fn set_message(
sr: &mut RhaiSignatureRequirement,
message: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let mut owned = std::mem::take(sr);
owned.message = message;
*sr = owned;
Ok(sr.clone())
}
#[rhai_fn(name = "signed_by", return_raw)]
pub fn set_signed_by(
sr: &mut RhaiSignatureRequirement,
signed_by: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned = std::mem::take(sr);
*sr = owned.signed_by(signed_by);
Ok(sr.clone())
}
#[rhai_fn(name = "signature", return_raw)]
pub fn set_signature(
sr: &mut RhaiSignatureRequirement,
signature: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned = std::mem::take(sr);
*sr = owned.signature(signature);
Ok(sr.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(
sr: &mut RhaiSignatureRequirement,
status: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned = std::mem::take(sr);
*sr = owned.status(status);
Ok(sr.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)]
pub fn get_id(s: &mut RhaiSignatureRequirement) -> INT {
s.base_data.id as INT
}
#[rhai_fn(get = "flow_step_id", pure)]
pub fn get_flow_step_id(s: &mut RhaiSignatureRequirement) -> INT {
s.flow_step_id as INT
}
#[rhai_fn(get = "public_key", pure)]
pub fn get_public_key(s: &mut RhaiSignatureRequirement) -> String {
s.public_key.clone()
}
#[rhai_fn(get = "message", pure)]
pub fn get_message(s: &mut RhaiSignatureRequirement) -> String {
s.message.clone()
}
#[rhai_fn(get = "signed_by", pure)]
pub fn get_signed_by(s: &mut RhaiSignatureRequirement) -> Option<String> {
s.signed_by.clone()
}
#[rhai_fn(get = "signature", pure)]
pub fn get_signature(s: &mut RhaiSignatureRequirement) -> Option<String> {
s.signature.clone()
}
#[rhai_fn(get = "status", pure)]
pub fn get_status(s: &mut RhaiSignatureRequirement) -> String {
s.status.clone()
}
}
pub fn register_signature_requirement_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiSignatureRequirement>();
let mut module = exported_module!(rhai_signature_requirement_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_signature_requirement",
resource_type_str: "SignatureRequirement",
rhai_return_rust_type: heromodels::models::flow::signature_requirement::SignatureRequirement
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_signature_requirement",
resource_type_str: "SignatureRequirement",
rhai_return_rust_type: heromodels::models::flow::signature_requirement::SignatureRequirement
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_signature_requirement",
resource_type_str: "SignatureRequirement",
rhai_return_rust_type: heromodels::models::flow::signature_requirement::SignatureRequirement
);
engine.register_global_module(module.into());
}