614 lines
20 KiB
Rust
614 lines
20 KiB
Rust
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<u32, Box<EvalAltResult>> {
|
|
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<u64, Box<EvalAltResult>> {
|
|
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<i32, Box<EvalAltResult>> {
|
|
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<OurDB>) {
|
|
// --- 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>("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>("SignerStatus"); // Expose the type itself
|
|
|
|
// --- ContractRevision ---
|
|
engine.register_type_with_name::<ContractRevision>("ContractRevision");
|
|
engine.register_fn(
|
|
"new_contract_revision",
|
|
move |context: NativeCallContext,
|
|
version_i64: i64,
|
|
content: String,
|
|
created_at_i64: i64,
|
|
created_by: String|
|
|
-> Result<ContractRevision, Box<EvalAltResult>> {
|
|
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<i64, Box<EvalAltResult>> {
|
|
Ok(revision.version as i64)
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"content",
|
|
|revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(revision.content.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"created_at",
|
|
|revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> {
|
|
Ok(revision.created_at as i64)
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"created_by",
|
|
|revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(revision.created_by.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"comments",
|
|
|revision: &mut ContractRevision| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
Ok(revision
|
|
.comments
|
|
.clone()
|
|
.map_or(Dynamic::UNIT, Dynamic::from))
|
|
},
|
|
);
|
|
|
|
// --- ContractSigner ---
|
|
engine.register_type_with_name::<ContractSigner>("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<ContractSigner, Box<EvalAltResult>> {
|
|
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<String, Box<EvalAltResult>> {
|
|
Ok(signer.id.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"name",
|
|
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(signer.name.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"email",
|
|
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(signer.email.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"status",
|
|
|signer: &mut ContractSigner| -> Result<SignerStatus, Box<EvalAltResult>> {
|
|
Ok(signer.status.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"signed_at_ts",
|
|
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
Ok(signer
|
|
.signed_at
|
|
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"comments",
|
|
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
Ok(signer.comments.clone().map_or(Dynamic::UNIT, Dynamic::from))
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"signed_at",
|
|
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
Ok(signer
|
|
.signed_at
|
|
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts)))
|
|
},
|
|
);
|
|
|
|
// --- Contract ---
|
|
engine.register_type_with_name::<Contract>("Contract");
|
|
engine.register_fn(
|
|
"new_contract",
|
|
move |context: NativeCallContext,
|
|
base_id_i64: i64,
|
|
contract_id: String|
|
|
-> Result<Contract, Box<EvalAltResult>> {
|
|
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<Contract, Box<EvalAltResult>> {
|
|
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<Contract, Box<EvalAltResult>> {
|
|
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<Contract, Box<EvalAltResult>> {
|
|
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<Contract, Box<EvalAltResult>> {
|
|
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::<ContractSigner>())
|
|
.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::<ContractRevision>())
|
|
.collect();
|
|
contract.revisions(revisions_vec)
|
|
},
|
|
);
|
|
|
|
engine.register_fn(
|
|
"current_version",
|
|
|context: NativeCallContext,
|
|
contract: Contract,
|
|
version_i64: i64|
|
|
-> Result<Contract, Box<EvalAltResult>> {
|
|
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<Contract, Box<EvalAltResult>> {
|
|
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<i64, Box<EvalAltResult>> {
|
|
Ok(contract.base_data.id as i64)
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"created_at_ts",
|
|
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
|
Ok(contract.base_data.created_at as i64)
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"updated_at_ts",
|
|
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
|
Ok(contract.base_data.modified_at as i64)
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"contract_id",
|
|
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(contract.contract_id.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"title",
|
|
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(contract.title.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"description",
|
|
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(contract.description.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"contract_type",
|
|
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(contract.contract_type.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"status",
|
|
|contract: &mut Contract| -> Result<ContractStatus, Box<EvalAltResult>> {
|
|
Ok(contract.status.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"created_by",
|
|
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(contract.created_by.clone())
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"terms_and_conditions",
|
|
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
|
Ok(contract.terms_and_conditions.clone())
|
|
},
|
|
);
|
|
|
|
engine.register_get(
|
|
"start_date",
|
|
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
Ok(contract
|
|
.start_date
|
|
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"end_date",
|
|
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
Ok(contract
|
|
.end_date
|
|
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"renewal_period_days",
|
|
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
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<Dynamic, Box<EvalAltResult>> {
|
|
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<Dynamic, Box<EvalAltResult>> {
|
|
Ok(contract
|
|
.last_signed_date
|
|
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
|
},
|
|
);
|
|
|
|
engine.register_get(
|
|
"current_version",
|
|
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
|
Ok(contract.current_version as i64)
|
|
},
|
|
);
|
|
|
|
engine.register_get(
|
|
"signers",
|
|
|contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
|
let rhai_array = contract
|
|
.signers
|
|
.iter()
|
|
.cloned()
|
|
.map(Dynamic::from)
|
|
.collect::<Array>();
|
|
Ok(rhai_array)
|
|
},
|
|
);
|
|
engine.register_get(
|
|
"revisions",
|
|
|contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
|
let rhai_array = contract
|
|
.revisions
|
|
.iter()
|
|
.cloned()
|
|
.map(Dynamic::from)
|
|
.collect::<Array>();
|
|
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<EvalAltResult>> {
|
|
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<Contract, Box<EvalAltResult>> {
|
|
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,
|
|
))
|
|
})
|
|
},
|
|
);
|
|
}
|