implement more models and rhai examples

This commit is contained in:
timurgordon
2025-05-22 03:57:03 +03:00
parent aa8ef90f9f
commit 56ec505874
79 changed files with 4546 additions and 182 deletions

View File

@@ -0,0 +1,56 @@
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use super::flow_step::FlowStep;
/// Represents a signing flow.
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[model]
pub struct Flow {
/// Base model data (id, created_at, updated_at).
pub base_data: BaseModelData,
/// A unique UUID for the flow, for external reference.
#[index]
pub flow_uuid: String,
/// Name of the flow.
#[index]
pub name: String,
/// Current status of the flow (e.g., "Pending", "InProgress", "Completed", "Failed").
pub status: String,
/// Steps involved in this flow.
pub steps: Vec<FlowStep>,
}
impl Flow {
/// Create a new flow.
/// The `id` is the database primary key.
/// The `flow_uuid` should be a Uuid::new_v4().to_string().
pub fn new(id: u32, flow_uuid: impl ToString) -> Self {
Self {
base_data: BaseModelData::new(id),
flow_uuid: flow_uuid.to_string(),
name: String::new(), // Default name, to be set by builder
status: String::from("Pending"), // Default status, to be set by builder
steps: Vec::new(),
}
}
pub fn name(mut self, name: impl ToString) -> Self {
self.name = name.to_string();
self
}
pub fn status(mut self, status: impl ToString) -> Self {
self.status = status.to_string();
self
}
pub fn add_step(mut self, step: FlowStep) -> Self {
self.steps.push(step);
self
}
}

View File

@@ -0,0 +1,44 @@
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
/// Represents a step within a signing flow.
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[model]
pub struct FlowStep {
/// Base model data.
pub base_data: BaseModelData,
/// Optional description for the step.
pub description: Option<String>,
/// Order of this step within the flow.
#[index]
pub step_order: u32,
/// Current status of the flow step (e.g., "Pending", "InProgress", "Completed", "Failed").
pub status: String,
}
impl FlowStep {
/// Create a new flow step.
pub fn new(id: u32, step_order: u32) -> Self {
Self {
base_data: BaseModelData::new(id),
description: None,
step_order,
status: String::from("Pending"), // Default status
}
}
/// Sets the description for the flow step.
pub fn description(mut self, description: impl ToString) -> Self {
self.description = Some(description.to_string());
self
}
pub fn status(mut self, status: impl ToString) -> Self {
self.status = status.to_string();
self
}
}

View File

@@ -0,0 +1,11 @@
// Export flow model submodules
pub mod flow;
pub mod flow_step;
pub mod signature_requirement;
pub mod rhai;
// Re-export key types for convenience
pub use flow::Flow;
pub use flow_step::FlowStep;
pub use signature_requirement::SignatureRequirement;
pub use rhai::register_flow_rhai_module;

View File

@@ -0,0 +1,140 @@
use rhai::{Dynamic, Engine, EvalAltResult, NativeCallContext, Position};
use std::sync::Arc;
use heromodels_core::BaseModelData;
use crate::db::hero::OurDB; // Import OurDB for actual DB operations
use crate::db::Collection; // Collection might be needed if we add more specific DB functions
use super::{
flow::Flow,
flow_step::FlowStep,
signature_requirement::SignatureRequirement,
};
// use rhai_wrapper::wrap_vec_return; // Not currently used for flow, but keep for potential future use.
// Helper function to convert Rhai's i64 to u32 for IDs
fn i64_to_u32(val: i64, context_pos: Position, field_name: &str, object_name: &str) -> Result<u32, Box<EvalAltResult>> {
val.try_into().map_err(|_e| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Conversion error for {} in {} from i64 to u32", field_name, object_name),
context_pos,
))
})
}
pub fn register_flow_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// --- Flow Model ---
// Constructor: new_flow(id: u32, flow_uuid: String)
engine.register_fn("new_flow", move |context: NativeCallContext, id_i64: i64, flow_uuid: String| -> Result<Flow, Box<EvalAltResult>> {
let id_u32 = i64_to_u32(id_i64, context.position(), "id", "new_flow")?;
Ok(Flow::new(id_u32, flow_uuid))
});
// Builder methods for Flow
engine.register_fn("name", |flow: Flow, name_val: String| -> Flow { flow.name(name_val) });
engine.register_fn("status", |flow: Flow, status_val: String| -> Flow { flow.status(status_val) });
engine.register_fn("add_step", |flow: Flow, step: FlowStep| -> Flow { flow.add_step(step) });
// Getters for Flow fields
engine.register_get("id", |flow: &mut Flow| -> Result<i64, Box<EvalAltResult>> { Ok(flow.base_data.id as i64) });
engine.register_get("base_data", |flow: &mut Flow| -> Result<BaseModelData, Box<EvalAltResult>> { Ok(flow.base_data.clone()) });
engine.register_get("flow_uuid", |flow: &mut Flow| -> Result<String, Box<EvalAltResult>> { Ok(flow.flow_uuid.clone()) });
engine.register_get("name", |flow: &mut Flow| -> Result<String, Box<EvalAltResult>> { Ok(flow.name.clone()) });
engine.register_get("status", |flow: &mut Flow| -> Result<String, Box<EvalAltResult>> { Ok(flow.status.clone()) });
engine.register_get("steps", |flow: &mut Flow| -> Result<rhai::Array, Box<EvalAltResult>> {
let rhai_array = flow.steps.iter().cloned().map(Dynamic::from).collect::<rhai::Array>();
Ok(rhai_array)
});
// --- FlowStep Model ---
// Constructor: new_flow_step(id: u32, step_order: u32)
engine.register_fn("new_flow_step", move |context: NativeCallContext, id_i64: i64, step_order_i64: i64| -> Result<FlowStep, Box<EvalAltResult>> {
let id_u32 = i64_to_u32(id_i64, context.position(), "id", "new_flow_step")?;
let step_order_u32 = i64_to_u32(step_order_i64, context.position(), "step_order", "new_flow_step")?;
Ok(FlowStep::new(id_u32, step_order_u32))
});
// Builder methods for FlowStep
engine.register_fn("description", |fs: FlowStep, desc_val: String| -> FlowStep { fs.description(desc_val) }); // Assuming FlowStep::description takes impl ToString
engine.register_fn("status", |fs: FlowStep, status_val: String| -> FlowStep { fs.status(status_val) });
// Getters for FlowStep fields
engine.register_get("id", |step: &mut FlowStep| -> Result<i64, Box<EvalAltResult>> { Ok(step.base_data.id as i64) });
engine.register_get("base_data", |step: &mut FlowStep| -> Result<BaseModelData, Box<EvalAltResult>> { Ok(step.base_data.clone()) });
engine.register_get("description", |step: &mut FlowStep| -> Result<Dynamic, Box<EvalAltResult>> { Ok(match step.description.clone() { Some(s) => Dynamic::from(s), None => Dynamic::from(()) }) });
engine.register_get("step_order", |step: &mut FlowStep| -> Result<i64, Box<EvalAltResult>> { Ok(step.step_order as i64) });
engine.register_get("status", |step: &mut FlowStep| -> Result<String, Box<EvalAltResult>> { Ok(step.status.clone()) });
// --- SignatureRequirement Model ---
// Constructor: new_signature_requirement(id: u32, flow_step_id: u32, public_key: String, message: String)
engine.register_fn("new_signature_requirement",
move |context: NativeCallContext, id_i64: i64, flow_step_id_i64: i64, public_key: String, message: String|
-> Result<SignatureRequirement, Box<EvalAltResult>> {
let id_u32 = i64_to_u32(id_i64, context.position(), "id", "new_signature_requirement")?;
let flow_step_id_u32 = i64_to_u32(flow_step_id_i64, context.position(), "flow_step_id", "new_signature_requirement")?;
Ok(SignatureRequirement::new(id_u32, flow_step_id_u32, public_key, message))
});
// Builder methods for SignatureRequirement
engine.register_fn("signed_by", |sr: SignatureRequirement, signed_by_val: String| -> SignatureRequirement { sr.signed_by(signed_by_val) }); // Assuming SR::signed_by takes impl ToString
engine.register_fn("signature", |sr: SignatureRequirement, sig_val: String| -> SignatureRequirement { sr.signature(sig_val) }); // Assuming SR::signature takes impl ToString
engine.register_fn("status", |sr: SignatureRequirement, status_val: String| -> SignatureRequirement { sr.status(status_val) });
// Getters for SignatureRequirement fields
engine.register_get("id", |sr: &mut SignatureRequirement| -> Result<i64, Box<EvalAltResult>> { Ok(sr.base_data.id as i64) });
engine.register_get("base_data", |sr: &mut SignatureRequirement| -> Result<BaseModelData, Box<EvalAltResult>> { Ok(sr.base_data.clone()) });
engine.register_get("flow_step_id", |sr: &mut SignatureRequirement| -> Result<i64, Box<EvalAltResult>> { Ok(sr.flow_step_id as i64) });
engine.register_get("public_key", |sr: &mut SignatureRequirement| -> Result<String, Box<EvalAltResult>> { Ok(sr.public_key.clone()) });
engine.register_get("message", |sr: &mut SignatureRequirement| -> Result<String, Box<EvalAltResult>> { Ok(sr.message.clone()) });
engine.register_get("signed_by", |sr: &mut SignatureRequirement| -> Result<Dynamic, Box<EvalAltResult>> { Ok(match sr.signed_by.clone() { Some(s) => Dynamic::from(s), None => Dynamic::from(()) }) });
engine.register_get("signature", |sr: &mut SignatureRequirement| -> Result<Dynamic, Box<EvalAltResult>> { Ok(match sr.signature.clone() { Some(s) => Dynamic::from(s), None => Dynamic::from(()) }) });
engine.register_get("status", |sr: &mut SignatureRequirement| -> Result<String, Box<EvalAltResult>> { Ok(sr.status.clone()) });
// --- BaseModelData Getters (if not already globally registered) ---
// Assuming these might be specific to the context or shadowed, explicit registration is safer.
engine.register_get("id", |bmd: &mut BaseModelData| -> Result<i64, Box<EvalAltResult>> { Ok(bmd.id as i64) });
engine.register_get("created_at", |bmd: &mut BaseModelData| -> Result<i64, Box<EvalAltResult>> { Ok(bmd.created_at) });
engine.register_get("modified_at", |bmd: &mut BaseModelData| -> Result<i64, Box<EvalAltResult>> { Ok(bmd.modified_at) });
// engine.register_get("comments", |bmd: &mut BaseModelData| Ok(bmd.comments.clone())); // Rhai might need specific handling for Vec<String>
// --- Database Interaction Functions ---
let captured_db_for_set_flow = Arc::clone(&db);
engine.register_fn("set_flow", move |flow: Flow| -> Result<(), Box<EvalAltResult>> {
captured_db_for_set_flow.set(&flow).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to set Flow (ID: {}): {}", flow.base_data.id, e).into(), Position::NONE))
})
});
let captured_db_for_get_flow = Arc::clone(&db);
engine.register_fn("get_flow_by_id", move |context: NativeCallContext, id_i64: i64| -> Result<Flow, Box<EvalAltResult>> {
let id_u32 = i64_to_u32(id_i64, context.position(), "id", "get_flow_by_id")?;
captured_db_for_get_flow.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Error getting Flow (ID: {}): {}", id_u32, e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Flow with ID {} not found", id_u32).into(), Position::NONE)))
});
// Add get_flows_by_uuid, flow_exists etc. as needed, using wrap_vec_return for Vec results.
// FlowStep DB functions are removed as FlowSteps are now part of Flow.
let captured_db_for_set_sig_req = Arc::clone(&db);
engine.register_fn("set_signature_requirement", move |sr: SignatureRequirement| -> Result<(), Box<EvalAltResult>> {
captured_db_for_set_sig_req.set(&sr).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to set SignatureRequirement (ID: {}): {}", sr.base_data.id, e).into(), Position::NONE))
})
});
let captured_db_for_get_sig_req = Arc::clone(&db);
engine.register_fn("get_signature_requirement_by_id",
move |context: NativeCallContext, id_i64: i64|
-> Result<SignatureRequirement, Box<EvalAltResult>> {
let id_u32 = i64_to_u32(id_i64, context.position(), "id", "get_signature_requirement_by_id")?;
captured_db_for_get_sig_req.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Error getting SignatureRequirement (ID: {}): {}", id_u32, e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("SignatureRequirement with ID {} not found", id_u32).into(), Position::NONE)))
});
println!("Flow Rhai module registered.");
}

View File

@@ -0,0 +1,62 @@
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
/// Represents a signature requirement for a flow step.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[model]
pub struct SignatureRequirement {
/// Base model data.
pub base_data: BaseModelData,
/// Foreign key to the FlowStep this requirement belongs to.
#[index]
pub flow_step_id: u32,
/// The public key required to sign the message.
pub public_key: String,
/// The plaintext message to be signed.
pub message: String,
/// The public key of the entity that signed the message, if signed.
pub signed_by: Option<String>,
/// The signature, if signed.
pub signature: Option<String>,
/// Current status of the signature requirement (e.g., "Pending", "SentToClient", "Signed", "Failed", "Error").
pub status: String,
}
impl SignatureRequirement {
/// Create a new signature requirement.
pub fn new(id: u32, flow_step_id: u32, public_key: impl ToString, message: impl ToString) -> Self {
Self {
base_data: BaseModelData::new(id),
flow_step_id,
public_key: public_key.to_string(),
message: message.to_string(),
signed_by: None,
signature: None,
status: String::from("Pending"), // Default status
}
}
/// Sets the public key of the signer.
pub fn signed_by(mut self, signed_by: impl ToString) -> Self {
self.signed_by = Some(signed_by.to_string());
self
}
/// Sets the signature.
pub fn signature(mut self, signature: impl ToString) -> Self {
self.signature = Some(signature.to_string());
self
}
pub fn status(mut self, status: impl ToString) -> Self {
self.status = status.to_string();
self
}
}