use rhai::{Array, Dynamic, Engine, EvalAltResult, Module, NativeCallContext, Position}; use std::sync::Arc; use crate::db::hero::OurDB; // Updated path based on compiler suggestion // use heromodels_core::BaseModelData; // Removed as fields are accessed via contract.base_data directly use crate::models::legal::{ Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus, }; use crate::db::Collection; // Import the Collection trait // --- Helper Functions for ID and Timestamp Conversion --- fn i64_to_u32( val: i64, context_pos: Position, field_name: &str, object_name: &str, ) -> Result> { val.try_into().map_err(|_e| { Box::new(EvalAltResult::ErrorArithmetic( format!( "Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u32", field_name, object_name, val ), context_pos, )) }) } fn i64_to_u64( val: i64, context_pos: Position, field_name: &str, object_name: &str, ) -> Result> { val.try_into().map_err(|_e| { Box::new(EvalAltResult::ErrorArithmetic( format!( "Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u64", field_name, object_name, val ), context_pos, )) }) } fn i64_to_i32( val: i64, context_pos: Position, field_name: &str, object_name: &str, ) -> Result> { val.try_into().map_err(|_e| { Box::new(EvalAltResult::ErrorArithmetic( format!( "Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to i32", field_name, object_name, val ), context_pos, )) }) } pub fn register_legal_rhai_module(engine: &mut Engine, db: Arc) { // --- ContractStatus Enum --- // Register ContractStatus enum as constants let mut contract_status_module = Module::new(); contract_status_module.set_var("Draft", ContractStatus::Draft); contract_status_module.set_var("PendingSignatures", ContractStatus::PendingSignatures); contract_status_module.set_var("Signed", ContractStatus::Signed); contract_status_module.set_var("Active", ContractStatus::Active); contract_status_module.set_var("Expired", ContractStatus::Expired); contract_status_module.set_var("Cancelled", ContractStatus::Cancelled); engine.register_static_module("ContractStatusConstants", contract_status_module.into()); engine.register_type_with_name::("ContractStatus"); // Expose the type itself // Register SignerStatus enum as constants let mut signer_status_module = Module::new(); signer_status_module.set_var("Pending", SignerStatus::Pending); signer_status_module.set_var("Signed", SignerStatus::Signed); signer_status_module.set_var("Rejected", SignerStatus::Rejected); engine.register_static_module("SignerStatusConstants", signer_status_module.into()); engine.register_type_with_name::("SignerStatus"); // Expose the type itself // --- ContractRevision --- engine.register_type_with_name::("ContractRevision"); engine.register_fn( "new_contract_revision", move |context: NativeCallContext, version_i64: i64, content: String, created_at_i64: i64, created_by: String| -> Result> { let version = i64_to_u32( version_i64, context.position(), "version", "new_contract_revision", )?; let created_at = i64_to_u64( created_at_i64, context.position(), "created_at", "new_contract_revision", )?; Ok(ContractRevision::new( version, content, created_at, created_by, )) }, ); engine.register_fn( "comments", |mut revision: ContractRevision, comments: String| -> ContractRevision { revision.comments = Some(comments); revision }, ); engine.register_get( "version", |revision: &mut ContractRevision| -> Result> { Ok(revision.version as i64) }, ); engine.register_get( "content", |revision: &mut ContractRevision| -> Result> { Ok(revision.content.clone()) }, ); engine.register_get( "created_at", |revision: &mut ContractRevision| -> Result> { Ok(revision.created_at as i64) }, ); engine.register_get( "created_by", |revision: &mut ContractRevision| -> Result> { Ok(revision.created_by.clone()) }, ); engine.register_get( "comments", |revision: &mut ContractRevision| -> Result> { Ok(revision .comments .clone() .map_or(Dynamic::UNIT, Dynamic::from)) }, ); // --- ContractSigner --- engine.register_type_with_name::("ContractSigner"); engine.register_fn( "new_contract_signer", |id: String, name: String, email: String| -> ContractSigner { ContractSigner::new(id, name, email) }, ); engine.register_fn( "status", |signer: ContractSigner, status: SignerStatus| -> ContractSigner { signer.status(status) }, ); engine.register_fn( "signed_at", |context: NativeCallContext, signer: ContractSigner, signed_at_i64: i64| -> Result> { let signed_at_u64 = i64_to_u64( signed_at_i64, context.position(), "signed_at", "ContractSigner.signed_at", )?; Ok(signer.signed_at(signed_at_u64)) }, ); engine.register_fn( "clear_signed_at", |signer: ContractSigner| -> ContractSigner { signer.clear_signed_at() }, ); engine.register_fn( "comments", |signer: ContractSigner, comments: String| -> ContractSigner { signer.comments(comments) }, ); engine.register_fn( "clear_comments", |signer: ContractSigner| -> ContractSigner { signer.clear_comments() }, ); engine.register_get( "id", |signer: &mut ContractSigner| -> Result> { Ok(signer.id.clone()) }, ); engine.register_get( "name", |signer: &mut ContractSigner| -> Result> { Ok(signer.name.clone()) }, ); engine.register_get( "email", |signer: &mut ContractSigner| -> Result> { Ok(signer.email.clone()) }, ); engine.register_get( "status", |signer: &mut ContractSigner| -> Result> { Ok(signer.status.clone()) }, ); engine.register_get( "signed_at_ts", |signer: &mut ContractSigner| -> Result> { Ok(signer .signed_at .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) }, ); engine.register_get( "comments", |signer: &mut ContractSigner| -> Result> { Ok(signer.comments.clone().map_or(Dynamic::UNIT, Dynamic::from)) }, ); engine.register_get( "signed_at", |signer: &mut ContractSigner| -> Result> { Ok(signer .signed_at .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts))) }, ); // --- Contract --- engine.register_type_with_name::("Contract"); engine.register_fn( "new_contract", move |context: NativeCallContext, base_id_i64: i64, contract_id: String| -> Result> { let base_id = i64_to_u32( base_id_i64, context.position(), "base_id", "new_contract", )?; Ok(Contract::new(base_id, contract_id)) }, ); // Builder methods engine.register_fn("title", |contract: Contract, title: String| -> Contract { contract.title(title) }); engine.register_fn( "description", |contract: Contract, description: String| -> Contract { contract.description(description) }, ); engine.register_fn( "contract_type", |contract: Contract, contract_type: String| -> Contract { contract.contract_type(contract_type) }, ); engine.register_fn( "status", |contract: Contract, status: ContractStatus| -> Contract { contract.status(status) }, ); engine.register_fn( "created_by", |contract: Contract, created_by: String| -> Contract { contract.created_by(created_by) }, ); engine.register_fn( "terms_and_conditions", |contract: Contract, terms: String| -> Contract { contract.terms_and_conditions(terms) }, ); engine.register_fn( "start_date", |context: NativeCallContext, contract: Contract, start_date_i64: i64| -> Result> { let start_date_u64 = i64_to_u64( start_date_i64, context.position(), "start_date", "Contract.start_date", )?; Ok(contract.start_date(start_date_u64)) }, ); engine.register_fn("clear_start_date", |contract: Contract| -> Contract { contract.clear_start_date() }); engine.register_fn( "end_date", |context: NativeCallContext, contract: Contract, end_date_i64: i64| -> Result> { let end_date_u64 = i64_to_u64( end_date_i64, context.position(), "end_date", "Contract.end_date", )?; Ok(contract.end_date(end_date_u64)) }, ); engine.register_fn("clear_end_date", |contract: Contract| -> Contract { contract.clear_end_date() }); engine.register_fn( "renewal_period_days", |context: NativeCallContext, contract: Contract, days_i64: i64| -> Result> { let days_i32 = i64_to_i32( days_i64, context.position(), "renewal_period_days", "Contract.renewal_period_days", )?; Ok(contract.renewal_period_days(days_i32)) }, ); engine.register_fn( "clear_renewal_period_days", |contract: Contract| -> Contract { contract.clear_renewal_period_days() }, ); engine.register_fn( "next_renewal_date", |context: NativeCallContext, contract: Contract, date_i64: i64| -> Result> { let date_u64 = i64_to_u64( date_i64, context.position(), "next_renewal_date", "Contract.next_renewal_date", )?; Ok(contract.next_renewal_date(date_u64)) }, ); engine.register_fn( "clear_next_renewal_date", |contract: Contract| -> Contract { contract.clear_next_renewal_date() }, ); engine.register_fn( "add_signer", |contract: Contract, signer: ContractSigner| -> Contract { contract.add_signer(signer) }, ); engine.register_fn( "signers", |contract: Contract, signers_array: Array| -> Contract { let signers_vec = signers_array .into_iter() .filter_map(|s| s.try_cast::()) .collect(); contract.signers(signers_vec) }, ); engine.register_fn( "add_revision", |contract: Contract, revision: ContractRevision| -> Contract { contract.add_revision(revision) }, ); engine.register_fn( "revisions", |contract: Contract, revisions_array: Array| -> Contract { let revisions_vec = revisions_array .into_iter() .filter_map(|r| r.try_cast::()) .collect(); contract.revisions(revisions_vec) }, ); engine.register_fn( "current_version", |context: NativeCallContext, contract: Contract, version_i64: i64| -> Result> { let version_u32 = i64_to_u32( version_i64, context.position(), "current_version", "Contract.current_version", )?; Ok(contract.current_version(version_u32)) }, ); engine.register_fn( "last_signed_date", |context: NativeCallContext, contract: Contract, date_i64: i64| -> Result> { let date_u64 = i64_to_u64( date_i64, context.position(), "last_signed_date", "Contract.last_signed_date", )?; Ok(contract.last_signed_date(date_u64)) }, ); engine.register_fn("clear_last_signed_date", |contract: Contract| -> Contract { contract.clear_last_signed_date() }); // Getters for Contract engine.register_get( "id", |contract: &mut Contract| -> Result> { Ok(contract.base_data.id as i64) }, ); engine.register_get( "created_at_ts", |contract: &mut Contract| -> Result> { Ok(contract.base_data.created_at as i64) }, ); engine.register_get( "updated_at_ts", |contract: &mut Contract| -> Result> { Ok(contract.base_data.modified_at as i64) }, ); engine.register_get( "contract_id", |contract: &mut Contract| -> Result> { Ok(contract.contract_id.clone()) }, ); engine.register_get( "title", |contract: &mut Contract| -> Result> { Ok(contract.title.clone()) }, ); engine.register_get( "description", |contract: &mut Contract| -> Result> { Ok(contract.description.clone()) }, ); engine.register_get( "contract_type", |contract: &mut Contract| -> Result> { Ok(contract.contract_type.clone()) }, ); engine.register_get( "status", |contract: &mut Contract| -> Result> { Ok(contract.status.clone()) }, ); engine.register_get( "created_by", |contract: &mut Contract| -> Result> { Ok(contract.created_by.clone()) }, ); engine.register_get( "terms_and_conditions", |contract: &mut Contract| -> Result> { Ok(contract.terms_and_conditions.clone()) }, ); engine.register_get( "start_date", |contract: &mut Contract| -> Result> { Ok(contract .start_date .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) }, ); engine.register_get( "end_date", |contract: &mut Contract| -> Result> { Ok(contract .end_date .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) }, ); engine.register_get( "renewal_period_days", |contract: &mut Contract| -> Result> { Ok(contract .renewal_period_days .map_or(Dynamic::UNIT, |days| Dynamic::from(days as i64))) }, ); engine.register_get( "next_renewal_date", |contract: &mut Contract| -> Result> { Ok(contract .next_renewal_date .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) }, ); engine.register_get( "last_signed_date", |contract: &mut Contract| -> Result> { Ok(contract .last_signed_date .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) }, ); engine.register_get( "current_version", |contract: &mut Contract| -> Result> { Ok(contract.current_version as i64) }, ); engine.register_get( "signers", |contract: &mut Contract| -> Result> { let rhai_array = contract .signers .iter() .cloned() .map(Dynamic::from) .collect::(); Ok(rhai_array) }, ); engine.register_get( "revisions", |contract: &mut Contract| -> Result> { let rhai_array = contract .revisions .iter() .cloned() .map(Dynamic::from) .collect::(); Ok(rhai_array) }, ); // Method set_status engine.register_fn( "set_contract_status", |contract: &mut Contract, status: ContractStatus| { contract.set_status(status); }, ); // --- Database Interaction --- let captured_db_for_set = Arc::clone(&db); engine.register_fn( "set_contract", move |contract: Contract| -> Result<(), Box> { captured_db_for_set.set(&contract).map(|_| ()).map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!( "Failed to set Contract (ID: {}): {:?}", contract.base_data.id, e ) .into(), Position::NONE, )) }) }, ); let captured_db_for_get = Arc::clone(&db); engine.register_fn( "get_contract_by_id", move |context: NativeCallContext, id_i64: i64| -> Result> { let id_u32 = i64_to_u32(id_i64, context.position(), "id", "get_contract_by_id")?; captured_db_for_get .get_by_id(id_u32) .map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Error getting Contract (ID: {}): {}", id_u32, e).into(), Position::NONE, )) })? .ok_or_else(|| { Box::new(EvalAltResult::ErrorRuntime( format!("Contract with ID {} not found", id_u32).into(), Position::NONE, )) }) }, ); }