implement more models and rhai examples
This commit is contained in:
@@ -71,3 +71,13 @@ impl<E> From<bincode::error::EncodeError> for Error<E> {
|
||||
Error::Encode(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: std::fmt::Debug + std::fmt::Display> std::fmt::Display for Error<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::DB(e) => write!(f, "Database error: {}", e),
|
||||
Error::Decode(e) => write!(f, "Failed to decode model: {}", e),
|
||||
Error::Encode(e) => write!(f, "Failed to encode model: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -228,7 +228,64 @@ where
|
||||
}
|
||||
|
||||
fn get_all(&self) -> Result<Vec<M>, super::Error<Self::Error>> {
|
||||
todo!("OurDB doesn't have a list all method yet")
|
||||
let mut index_db = self.index.lock().expect("can lock index DB");
|
||||
let mut data_db = self.data.lock().expect("can lock data DB");
|
||||
|
||||
let prefix = M::db_prefix();
|
||||
let mut all_object_ids: HashSet<u32> = HashSet::new();
|
||||
|
||||
// Use getall to find all index entries (values are serialized HashSet<u32>) for the given model prefix.
|
||||
match index_db.getall(prefix) {
|
||||
Ok(list_of_raw_ids_set_bytes) => {
|
||||
for raw_ids_set_bytes in list_of_raw_ids_set_bytes {
|
||||
// Each item in the list is a bincode-serialized HashSet<u32> of object IDs.
|
||||
match bincode::serde::decode_from_slice::<HashSet<u32>, _>(&raw_ids_set_bytes, BINCODE_CONFIG) {
|
||||
Ok((ids_set, _)) => { // Destructure the tuple (HashSet<u32>, usize)
|
||||
all_object_ids.extend(ids_set);
|
||||
}
|
||||
Err(e) => {
|
||||
// If deserialization of an ID set fails, propagate as a decode error.
|
||||
return Err(super::Error::Decode(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(tst::Error::PrefixNotFound(_)) => {
|
||||
// No index entries found for this prefix, meaning no objects of this type exist.
|
||||
// Note: tst::getall might return Ok(vec![]) in this case instead of PrefixNotFound.
|
||||
// Depending on tst implementation, this arm might be redundant if getall returns empty vec.
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
Err(e) => {
|
||||
// Other TST errors.
|
||||
return Err(super::Error::DB(e));
|
||||
}
|
||||
}
|
||||
|
||||
let mut results: Vec<M> = Vec::with_capacity(all_object_ids.len());
|
||||
for obj_id in all_object_ids {
|
||||
match Self::get_ourdb_value::<M>(&mut data_db, obj_id) {
|
||||
Ok(Some(obj)) => {
|
||||
results.push(obj);
|
||||
}
|
||||
Ok(None) => {
|
||||
// This case implies an inconsistency: an object ID was in an index,
|
||||
// but the object itself was not found in the data store.
|
||||
// Log this an issue, but continue processing other valid objects.
|
||||
// Consider how strictly this should be handled (e.g., return error or log and skip).
|
||||
eprintln!(
|
||||
"[heromodels] Warning: Object ID {} found in index for model '{}' but not in data store.",
|
||||
obj_id,
|
||||
M::db_prefix()
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
// If fetching or decoding a specific object fails.
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
}
|
||||
|
||||
|
178
heromodels/src/models/biz/company.rs
Normal file
178
heromodels/src/models/biz/company.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::{BaseModelData, Model, IndexKey, IndexKeyBuilder, Index};
|
||||
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
|
||||
|
||||
// --- Enums ---
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum CompanyStatus {
|
||||
Active,
|
||||
Inactive,
|
||||
Suspended,
|
||||
}
|
||||
|
||||
impl Default for CompanyStatus {
|
||||
fn default() -> Self {
|
||||
CompanyStatus::Inactive
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum BusinessType {
|
||||
Coop,
|
||||
Single,
|
||||
Twin,
|
||||
Starter,
|
||||
Global,
|
||||
}
|
||||
|
||||
impl Default for BusinessType {
|
||||
fn default() -> Self {
|
||||
BusinessType::Single
|
||||
}
|
||||
}
|
||||
|
||||
// --- Company Struct ---
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, CustomType)] // Added CustomType
|
||||
pub struct Company {
|
||||
pub base_data: BaseModelData,
|
||||
pub name: String,
|
||||
pub registration_number: String,
|
||||
pub incorporation_date: i64, // Changed to i64 // Timestamp
|
||||
pub fiscal_year_end: String, // e.g., "MM-DD"
|
||||
pub email: String,
|
||||
pub phone: String,
|
||||
pub website: String,
|
||||
pub address: String,
|
||||
pub business_type: BusinessType,
|
||||
pub industry: String,
|
||||
pub description: String,
|
||||
pub status: CompanyStatus,
|
||||
}
|
||||
|
||||
// --- Model Trait Implementation ---
|
||||
|
||||
impl Model for Company {
|
||||
fn db_prefix() -> &'static str {
|
||||
"company"
|
||||
}
|
||||
|
||||
fn get_id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
fn base_data_mut(&mut self) -> &mut BaseModelData {
|
||||
&mut self.base_data
|
||||
}
|
||||
|
||||
// Override db_keys to provide custom indexes if needed
|
||||
fn db_keys(&self) -> Vec<IndexKey> {
|
||||
vec![
|
||||
IndexKeyBuilder::new("name").value(self.name.clone()).build(),
|
||||
IndexKeyBuilder::new("registration_number").value(self.registration_number.clone()).build(),
|
||||
// Add other relevant keys, e.g., by status or type if frequently queried
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// --- Index Implementations (Example) ---
|
||||
|
||||
pub struct CompanyNameIndex;
|
||||
impl Index for CompanyNameIndex {
|
||||
type Model = Company;
|
||||
type Key = str;
|
||||
fn key() -> &'static str {
|
||||
"name"
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompanyRegistrationNumberIndex;
|
||||
impl Index for CompanyRegistrationNumberIndex {
|
||||
type Model = Company;
|
||||
type Key = str;
|
||||
fn key() -> &'static str {
|
||||
"registration_number"
|
||||
}
|
||||
}
|
||||
|
||||
// --- Builder Pattern ---
|
||||
|
||||
impl Company {
|
||||
pub fn new(id: u32, name: String, registration_number: String, incorporation_date: i64) -> Self { // incorporation_date to i64
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
name,
|
||||
registration_number,
|
||||
incorporation_date, // This is i64 now
|
||||
fiscal_year_end: String::new(),
|
||||
email: String::new(),
|
||||
phone: String::new(),
|
||||
website: String::new(),
|
||||
address: String::new(),
|
||||
business_type: BusinessType::default(),
|
||||
industry: String::new(),
|
||||
description: String::new(),
|
||||
status: CompanyStatus::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fiscal_year_end(mut self, fiscal_year_end: String) -> Self {
|
||||
self.fiscal_year_end = fiscal_year_end;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn email(mut self, email: String) -> Self {
|
||||
self.email = email;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn phone(mut self, phone: String) -> Self {
|
||||
self.phone = phone;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn website(mut self, website: String) -> Self {
|
||||
self.website = website;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn address(mut self, address: String) -> Self {
|
||||
self.address = address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn business_type(mut self, business_type: BusinessType) -> Self {
|
||||
self.business_type = business_type;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn industry(mut self, industry: String) -> Self {
|
||||
self.industry = industry;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn description(mut self, description: String) -> Self {
|
||||
self.description = description;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn status(mut self, status: CompanyStatus) -> Self {
|
||||
self.status = status;
|
||||
self
|
||||
}
|
||||
|
||||
// Setter for base_data fields if needed directly, though usually handled by Model trait or new()
|
||||
// Builder methods for created_at and modified_at can be more specific if needed,
|
||||
// but Model::build() updates modified_at, and BaseModelData::new() sets created_at.
|
||||
// If direct setting is required for tests or specific scenarios:
|
||||
pub fn set_base_created_at(mut self, created_at: i64) -> Self {
|
||||
self.base_data.created_at = created_at;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_base_modified_at(mut self, modified_at: i64) -> Self {
|
||||
self.base_data.modified_at = modified_at;
|
||||
self
|
||||
}
|
||||
}
|
25
heromodels/src/models/biz/mod.rs
Normal file
25
heromodels/src/models/biz/mod.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Business models module
|
||||
// Sub-modules will be declared here
|
||||
|
||||
pub mod company;
|
||||
pub mod product;
|
||||
// pub mod sale;
|
||||
// pub mod shareholder;
|
||||
// pub mod user;
|
||||
|
||||
// Re-export main types from sub-modules
|
||||
pub use company::{Company, CompanyStatus, BusinessType};
|
||||
pub mod shareholder;
|
||||
pub use shareholder::{Shareholder, ShareholderType};
|
||||
pub use product::{Product, ProductType, ProductStatus, ProductComponent};
|
||||
|
||||
pub mod sale;
|
||||
pub use sale::{Sale, SaleItem, SaleStatus};
|
||||
|
||||
// pub use user::{User}; // Assuming a simple User model for now
|
||||
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub mod rhai;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use rhai::register_biz_rhai_module;
|
179
heromodels/src/models/biz/product.rs
Normal file
179
heromodels/src/models/biz/product.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_core::Model;
|
||||
|
||||
// ProductType represents the type of a product
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub enum ProductType {
|
||||
#[default]
|
||||
Product,
|
||||
Service,
|
||||
}
|
||||
|
||||
// ProductStatus represents the status of a product
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub enum ProductStatus {
|
||||
#[default]
|
||||
Available,
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
// ProductComponent represents a component or sub-part of a product.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub struct ProductComponent {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub quantity: u32,
|
||||
}
|
||||
|
||||
impl ProductComponent {
|
||||
// Minimal constructor
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description: String::new(),
|
||||
quantity: 1, // Default quantity to 1
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods
|
||||
pub fn description(mut self, description: String) -> Self {
|
||||
self.description = description;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn quantity(mut self, quantity: u32) -> Self {
|
||||
self.quantity = quantity;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Product represents a product or service offered
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Product {
|
||||
pub base_data: BaseModelData,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub price: f64, // Representing currency.Currency for now
|
||||
pub type_: ProductType,
|
||||
pub category: String,
|
||||
pub status: ProductStatus,
|
||||
pub max_amount: u16,
|
||||
pub purchase_till: i64, // Representing ourtime.OurTime
|
||||
pub active_till: i64, // Representing ourtime.OurTime
|
||||
pub components: Vec<ProductComponent>,
|
||||
}
|
||||
|
||||
impl Model for Product {
|
||||
fn get_id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
fn db_prefix() -> &'static str {
|
||||
"prod"
|
||||
}
|
||||
|
||||
fn base_data_mut(&mut self) -> &mut BaseModelData {
|
||||
&mut self.base_data
|
||||
}
|
||||
}
|
||||
|
||||
impl Product {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
name: String::new(),
|
||||
description: String::new(),
|
||||
price: 0.0,
|
||||
type_: ProductType::default(),
|
||||
category: String::new(),
|
||||
status: ProductStatus::default(),
|
||||
max_amount: 0,
|
||||
purchase_till: 0,
|
||||
active_till: 0,
|
||||
components: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn description(mut self, description: String) -> Self {
|
||||
self.description = description;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn price(mut self, price: f64) -> Self {
|
||||
self.price = price;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn type_(mut self, type_: ProductType) -> Self {
|
||||
self.type_ = type_;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn category(mut self, category: String) -> Self {
|
||||
self.category = category;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn status(mut self, status: ProductStatus) -> Self {
|
||||
self.status = status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn max_amount(mut self, max_amount: u16) -> Self {
|
||||
self.max_amount = max_amount;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn purchase_till(mut self, purchase_till: i64) -> Self {
|
||||
self.purchase_till = purchase_till;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn active_till(mut self, active_till: i64) -> Self {
|
||||
self.active_till = active_till;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_component(mut self, component: ProductComponent) -> Self {
|
||||
self.components.push(component);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn components(mut self, components: Vec<ProductComponent>) -> Self {
|
||||
self.components = components;
|
||||
self
|
||||
}
|
||||
|
||||
// BaseModelData field setters
|
||||
pub fn set_base_created_at(mut self, time: i64) -> Self {
|
||||
self.base_data.created_at = time;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_base_modified_at(mut self, time: i64) -> Self {
|
||||
self.base_data.modified_at = time;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_base_comment_id(mut self, comment_id: u32) -> Self {
|
||||
self.base_data.comments.push(comment_id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_base_comment_ids(mut self, comment_ids: Vec<u32>) -> Self {
|
||||
self.base_data.comments = comment_ids;
|
||||
self
|
||||
}
|
||||
}
|
324
heromodels/src/models/biz/rhai.rs
Normal file
324
heromodels/src/models/biz/rhai.rs
Normal file
@@ -0,0 +1,324 @@
|
||||
use rhai::{Engine, Module, Dynamic, EvalAltResult, Position};
|
||||
use std::sync::Arc;
|
||||
use crate::db::Collection; // For db.set and db.get_by_id
|
||||
use crate::db::hero::OurDB;
|
||||
use super::company::{Company, CompanyStatus, BusinessType};
|
||||
use crate::models::biz::shareholder::{Shareholder, ShareholderType};
|
||||
use crate::models::biz::product::{Product, ProductType, ProductStatus, ProductComponent};
|
||||
use crate::models::biz::sale::{Sale, SaleItem, SaleStatus};
|
||||
use heromodels_core::Model;
|
||||
|
||||
// Helper function to convert i64 to u32, returning a Rhai error if conversion fails
|
||||
fn id_from_i64(id_val: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_val).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert i64 '{}' to u32 for ID", id_val),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn register_biz_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
let module = Module::new();
|
||||
|
||||
// --- Enum Constants: CompanyStatus ---
|
||||
let mut status_constants_module = Module::new();
|
||||
status_constants_module.set_var("Active", Dynamic::from(CompanyStatus::Active.clone()));
|
||||
status_constants_module.set_var("Inactive", Dynamic::from(CompanyStatus::Inactive.clone()));
|
||||
status_constants_module.set_var("Suspended", Dynamic::from(CompanyStatus::Suspended.clone()));
|
||||
engine.register_static_module("CompanyStatusConstants", status_constants_module.into());
|
||||
engine.register_type_with_name::<CompanyStatus>("CompanyStatus");
|
||||
|
||||
// --- Enum Constants: BusinessType ---
|
||||
let mut business_type_constants_module = Module::new();
|
||||
business_type_constants_module.set_var("Coop", Dynamic::from(BusinessType::Coop.clone()));
|
||||
business_type_constants_module.set_var("Single", Dynamic::from(BusinessType::Single.clone()));
|
||||
business_type_constants_module.set_var("Twin", Dynamic::from(BusinessType::Twin.clone()));
|
||||
business_type_constants_module.set_var("Starter", Dynamic::from(BusinessType::Starter.clone()));
|
||||
business_type_constants_module.set_var("Global", Dynamic::from(BusinessType::Global.clone()));
|
||||
engine.register_static_module("BusinessTypeConstants", business_type_constants_module.into());
|
||||
engine.register_type_with_name::<BusinessType>("BusinessType");
|
||||
|
||||
// --- Company ---
|
||||
engine.register_type_with_name::<Company>("Company");
|
||||
|
||||
// Constructor
|
||||
engine.register_fn("new_company", |id: i64, name: String, registration_number: String, incorporation_date: i64| -> Result<Company, Box<EvalAltResult>> { Ok(Company::new(id as u32, name, registration_number, incorporation_date)) });
|
||||
|
||||
// Getters for Company
|
||||
engine.register_get("id", |company: &mut Company| -> Result<i64, Box<EvalAltResult>> { Ok(company.get_id() as i64) });
|
||||
engine.register_get("created_at", |company: &mut Company| -> Result<i64, Box<EvalAltResult>> { Ok(company.base_data.created_at) });
|
||||
engine.register_get("modified_at", |company: &mut Company| -> Result<i64, Box<EvalAltResult>> { Ok(company.base_data.modified_at) });
|
||||
engine.register_get("name", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.name.clone()) });
|
||||
engine.register_get("registration_number", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.registration_number.clone()) });
|
||||
engine.register_get("incorporation_date", |company: &mut Company| -> Result<i64, Box<EvalAltResult>> { Ok(company.incorporation_date as i64) });
|
||||
engine.register_get("fiscal_year_end", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.fiscal_year_end.clone()) });
|
||||
engine.register_get("email", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.email.clone()) });
|
||||
engine.register_get("phone", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.phone.clone()) });
|
||||
engine.register_get("website", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.website.clone()) });
|
||||
engine.register_get("address", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.address.clone()) });
|
||||
engine.register_get("business_type", |company: &mut Company| -> Result<BusinessType, Box<EvalAltResult>> { Ok(company.business_type.clone()) });
|
||||
engine.register_get("industry", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.industry.clone()) });
|
||||
engine.register_get("description", |company: &mut Company| -> Result<String, Box<EvalAltResult>> { Ok(company.description.clone()) });
|
||||
engine.register_get("status", |company: &mut Company| -> Result<CompanyStatus, Box<EvalAltResult>> { Ok(company.status.clone()) });
|
||||
// Builder methods for Company
|
||||
engine.register_fn("fiscal_year_end", |company: Company, fiscal_year_end: String| -> Result<Company, Box<EvalAltResult>> { Ok(company.fiscal_year_end(fiscal_year_end)) });
|
||||
engine.register_fn("email", |company: Company, email: String| -> Result<Company, Box<EvalAltResult>> { Ok(company.email(email)) });
|
||||
engine.register_fn("phone", |company: Company, phone: String| -> Result<Company, Box<EvalAltResult>> { Ok(company.phone(phone)) });
|
||||
engine.register_fn("website", |company: Company, website: String| -> Result<Company, Box<EvalAltResult>> { Ok(company.website(website)) });
|
||||
engine.register_fn("address", |company: Company, address: String| -> Result<Company, Box<EvalAltResult>> { Ok(company.address(address)) });
|
||||
engine.register_fn("business_type", |company: Company, business_type: BusinessType| -> Result<Company, Box<EvalAltResult>> { Ok(company.business_type(business_type)) });
|
||||
engine.register_fn("industry", |company: Company, industry: String| -> Result<Company, Box<EvalAltResult>> { Ok(company.industry(industry)) });
|
||||
engine.register_fn("description", |company: Company, description: String| -> Result<Company, Box<EvalAltResult>> { Ok(company.description(description)) });
|
||||
engine.register_fn("status", |company: Company, status: CompanyStatus| -> Result<Company, Box<EvalAltResult>> { Ok(company.status(status)) });
|
||||
engine.register_fn("set_base_created_at", |company: Company, created_at: i64| -> Result<Company, Box<EvalAltResult>> { Ok(company.set_base_created_at(created_at)) });
|
||||
engine.register_fn("set_base_modified_at", |company: Company, modified_at: i64| -> Result<Company, Box<EvalAltResult>> { Ok(company.set_base_modified_at(modified_at)) });
|
||||
|
||||
// --- Enum Constants: ShareholderType ---
|
||||
let mut shareholder_type_constants_module = Module::new();
|
||||
shareholder_type_constants_module.set_var("Individual", Dynamic::from(ShareholderType::Individual.clone()));
|
||||
shareholder_type_constants_module.set_var("Corporate", Dynamic::from(ShareholderType::Corporate.clone()));
|
||||
engine.register_static_module("ShareholderTypeConstants", shareholder_type_constants_module.into());
|
||||
engine.register_type_with_name::<ShareholderType>("ShareholderType");
|
||||
|
||||
// --- Shareholder ---
|
||||
engine.register_type_with_name::<Shareholder>("Shareholder");
|
||||
|
||||
// Constructor for Shareholder (minimal, takes only ID)
|
||||
engine.register_fn("new_shareholder", |id: i64| -> Result<Shareholder, Box<EvalAltResult>> {
|
||||
Ok(Shareholder::new(id as u32))
|
||||
});
|
||||
|
||||
// Getters for Shareholder
|
||||
engine.register_get("id", |shareholder: &mut Shareholder| -> Result<i64, Box<EvalAltResult>> { Ok(shareholder.get_id() as i64) });
|
||||
engine.register_get("created_at", |shareholder: &mut Shareholder| -> Result<i64, Box<EvalAltResult>> { Ok(shareholder.base_data.created_at) });
|
||||
engine.register_get("modified_at", |shareholder: &mut Shareholder| -> Result<i64, Box<EvalAltResult>> { Ok(shareholder.base_data.modified_at) });
|
||||
engine.register_get("company_id", |shareholder: &mut Shareholder| -> Result<i64, Box<EvalAltResult>> { Ok(shareholder.company_id as i64) });
|
||||
engine.register_get("user_id", |shareholder: &mut Shareholder| -> Result<i64, Box<EvalAltResult>> { Ok(shareholder.user_id as i64) });
|
||||
engine.register_get("name", |shareholder: &mut Shareholder| -> Result<String, Box<EvalAltResult>> { Ok(shareholder.name.clone()) });
|
||||
engine.register_get("shares", |shareholder: &mut Shareholder| -> Result<f64, Box<EvalAltResult>> { Ok(shareholder.shares) });
|
||||
engine.register_get("percentage", |shareholder: &mut Shareholder| -> Result<f64, Box<EvalAltResult>> { Ok(shareholder.percentage) });
|
||||
engine.register_get("type_", |shareholder: &mut Shareholder| -> Result<ShareholderType, Box<EvalAltResult>> { Ok(shareholder.type_.clone()) });
|
||||
engine.register_get("since", |shareholder: &mut Shareholder| -> Result<i64, Box<EvalAltResult>> { Ok(shareholder.since) });
|
||||
|
||||
// Builder methods for Shareholder
|
||||
engine.register_fn("company_id", |shareholder: Shareholder, company_id: i64| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.company_id(company_id as u32)) });
|
||||
engine.register_fn("user_id", |shareholder: Shareholder, user_id: i64| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.user_id(user_id as u32)) });
|
||||
engine.register_fn("name", |shareholder: Shareholder, name: String| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.name(name)) });
|
||||
engine.register_fn("shares", |shareholder: Shareholder, shares: f64| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.shares(shares)) });
|
||||
engine.register_fn("percentage", |shareholder: Shareholder, percentage: f64| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.percentage(percentage)) });
|
||||
engine.register_fn("type_", |shareholder: Shareholder, type_: ShareholderType| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.type_(type_)) });
|
||||
engine.register_fn("since", |shareholder: Shareholder, since: i64| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.since(since)) });
|
||||
engine.register_fn("set_base_created_at", |shareholder: Shareholder, created_at: i64| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.set_base_created_at(created_at)) });
|
||||
engine.register_fn("set_base_modified_at", |shareholder: Shareholder, modified_at: i64| -> Result<Shareholder, Box<EvalAltResult>> { Ok(shareholder.set_base_modified_at(modified_at)) });
|
||||
|
||||
|
||||
// --- Enum Constants: ProductType ---
|
||||
let mut product_type_constants_module = Module::new();
|
||||
product_type_constants_module.set_var("Product", Dynamic::from(ProductType::Product.clone()));
|
||||
product_type_constants_module.set_var("Service", Dynamic::from(ProductType::Service.clone()));
|
||||
engine.register_static_module("ProductTypeConstants", product_type_constants_module.into());
|
||||
engine.register_type_with_name::<ProductType>("ProductType");
|
||||
|
||||
// --- Enum Constants: ProductStatus ---
|
||||
let mut product_status_constants_module = Module::new();
|
||||
product_status_constants_module.set_var("Available", Dynamic::from(ProductStatus::Available.clone()));
|
||||
product_status_constants_module.set_var("Unavailable", Dynamic::from(ProductStatus::Unavailable.clone()));
|
||||
engine.register_static_module("ProductStatusConstants", product_status_constants_module.into());
|
||||
engine.register_type_with_name::<ProductStatus>("ProductStatus");
|
||||
|
||||
// --- Enum Constants: SaleStatus ---
|
||||
let mut sale_status_module = Module::new();
|
||||
sale_status_module.set_var("Pending", Dynamic::from(SaleStatus::Pending.clone()));
|
||||
sale_status_module.set_var("Completed", Dynamic::from(SaleStatus::Completed.clone()));
|
||||
sale_status_module.set_var("Cancelled", Dynamic::from(SaleStatus::Cancelled.clone()));
|
||||
engine.register_static_module("SaleStatusConstants", sale_status_module.into());
|
||||
engine.register_type_with_name::<SaleStatus>("SaleStatus");
|
||||
|
||||
// --- ProductComponent ---
|
||||
engine.register_type_with_name::<ProductComponent>("ProductComponent")
|
||||
.register_fn("new_product_component", |name: String| -> Result<ProductComponent, Box<EvalAltResult>> { Ok(ProductComponent::new(name)) })
|
||||
.register_get("name", |pc: &mut ProductComponent| -> Result<String, Box<EvalAltResult>> { Ok(pc.name.clone()) })
|
||||
.register_fn("name", |pc: ProductComponent, name: String| -> Result<ProductComponent, Box<EvalAltResult>> { Ok(pc.name(name)) })
|
||||
.register_get("description", |pc: &mut ProductComponent| -> Result<String, Box<EvalAltResult>> { Ok(pc.description.clone()) })
|
||||
.register_fn("description", |pc: ProductComponent, description: String| -> Result<ProductComponent, Box<EvalAltResult>> { Ok(pc.description(description)) })
|
||||
.register_get("quantity", |pc: &mut ProductComponent| -> Result<i64, Box<EvalAltResult>> { Ok(pc.quantity as i64) })
|
||||
.register_fn("quantity", |pc: ProductComponent, quantity: i64| -> Result<ProductComponent, Box<EvalAltResult>> { Ok(pc.quantity(quantity as u32)) });
|
||||
|
||||
// --- Product ---
|
||||
engine.register_type_with_name::<Product>("Product")
|
||||
.register_fn("new_product", |id: i64| -> Result<Product, Box<EvalAltResult>> { Ok(Product::new(id as u32)) })
|
||||
// Getters for Product
|
||||
.register_get("id", |p: &mut Product| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.id as i64) })
|
||||
.register_get("name", |p: &mut Product| -> Result<String, Box<EvalAltResult>> { Ok(p.name.clone()) })
|
||||
.register_get("description", |p: &mut Product| -> Result<String, Box<EvalAltResult>> { Ok(p.description.clone()) })
|
||||
.register_get("price", |p: &mut Product| -> Result<f64, Box<EvalAltResult>> { Ok(p.price) })
|
||||
.register_get("type_", |p: &mut Product| -> Result<ProductType, Box<EvalAltResult>> { Ok(p.type_.clone()) })
|
||||
.register_get("category", |p: &mut Product| -> Result<String, Box<EvalAltResult>> { Ok(p.category.clone()) })
|
||||
.register_get("status", |p: &mut Product| -> Result<ProductStatus, Box<EvalAltResult>> { Ok(p.status.clone()) })
|
||||
.register_get("max_amount", |p: &mut Product| -> Result<i64, Box<EvalAltResult>> { Ok(p.max_amount as i64) })
|
||||
.register_get("purchase_till", |p: &mut Product| -> Result<i64, Box<EvalAltResult>> { Ok(p.purchase_till) })
|
||||
.register_get("active_till", |p: &mut Product| -> Result<i64, Box<EvalAltResult>> { Ok(p.active_till) })
|
||||
.register_get("components", |p: &mut Product| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
let rhai_array = p.components.iter().cloned().map(Dynamic::from).collect::<rhai::Array>();
|
||||
Ok(rhai_array)
|
||||
})
|
||||
// Getters for BaseModelData fields
|
||||
.register_get("created_at", |p: &mut Product| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.created_at) })
|
||||
.register_get("modified_at", |p: &mut Product| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.modified_at) })
|
||||
.register_get("comments", |p: &mut Product| -> Result<Vec<i64>, Box<EvalAltResult>> { Ok(p.base_data.comments.iter().map(|&id| id as i64).collect()) })
|
||||
// Builder methods for Product
|
||||
.register_fn("name", |p: Product, name: String| -> Result<Product, Box<EvalAltResult>> { Ok(p.name(name)) })
|
||||
.register_fn("description", |p: Product, description: String| -> Result<Product, Box<EvalAltResult>> { Ok(p.description(description)) })
|
||||
.register_fn("price", |p: Product, price: f64| -> Result<Product, Box<EvalAltResult>> { Ok(p.price(price)) })
|
||||
.register_fn("type_", |p: Product, type_: ProductType| -> Result<Product, Box<EvalAltResult>> { Ok(p.type_(type_)) })
|
||||
.register_fn("category", |p: Product, category: String| -> Result<Product, Box<EvalAltResult>> { Ok(p.category(category)) })
|
||||
.register_fn("status", |p: Product, status: ProductStatus| -> Result<Product, Box<EvalAltResult>> { Ok(p.status(status)) })
|
||||
.register_fn("max_amount", |p: Product, max_amount: i64| -> Result<Product, Box<EvalAltResult>> { Ok(p.max_amount(max_amount as u16)) })
|
||||
.register_fn("purchase_till", |p: Product, purchase_till: i64| -> Result<Product, Box<EvalAltResult>> { Ok(p.purchase_till(purchase_till)) })
|
||||
.register_fn("active_till", |p: Product, active_till: i64| -> Result<Product, Box<EvalAltResult>> { Ok(p.active_till(active_till)) })
|
||||
.register_fn("add_component", |p: Product, component: ProductComponent| -> Result<Product, Box<EvalAltResult>> { Ok(p.add_component(component)) })
|
||||
.register_fn("components", |p: Product, components: Vec<ProductComponent>| -> Result<Product, Box<EvalAltResult>> { Ok(p.components(components)) })
|
||||
.register_fn("set_base_created_at", |p: Product, time: i64| -> Result<Product, Box<EvalAltResult>> { Ok(p.set_base_created_at(time)) })
|
||||
.register_fn("set_base_modified_at", |p: Product, time: i64| -> Result<Product, Box<EvalAltResult>> { Ok(p.set_base_modified_at(time)) })
|
||||
.register_fn("add_base_comment_id", |p: Product, comment_id: i64| -> Result<Product, Box<EvalAltResult>> { Ok(p.add_base_comment_id(id_from_i64(comment_id)?)) })
|
||||
.register_fn("set_base_comment_ids", |p: Product, comment_ids: Vec<i64>| -> Result<Product, Box<EvalAltResult>> {
|
||||
let u32_ids = comment_ids.into_iter().map(id_from_i64).collect::<Result<Vec<u32>, _>>()?;
|
||||
Ok(p.set_base_comment_ids(u32_ids))
|
||||
});
|
||||
|
||||
// --- SaleItem ---
|
||||
engine.register_type_with_name::<SaleItem>("SaleItem");
|
||||
engine.register_fn("new_sale_item", |product_id_i64: i64, name: String, quantity_i64: i64, unit_price: f64, subtotal: f64| -> Result<SaleItem, Box<EvalAltResult>> {
|
||||
Ok(SaleItem::new(id_from_i64(product_id_i64)?, name, quantity_i64 as i32, unit_price, subtotal))
|
||||
});
|
||||
|
||||
// Getters for SaleItem
|
||||
engine.register_get("product_id", |si: &mut SaleItem| -> Result<i64, Box<EvalAltResult>> { Ok(si.product_id as i64) });
|
||||
engine.register_get("name", |si: &mut SaleItem| -> Result<String, Box<EvalAltResult>> { Ok(si.name.clone()) });
|
||||
engine.register_get("quantity", |si: &mut SaleItem| -> Result<i64, Box<EvalAltResult>> { Ok(si.quantity as i64) });
|
||||
engine.register_get("unit_price", |si: &mut SaleItem| -> Result<f64, Box<EvalAltResult>> { Ok(si.unit_price) });
|
||||
engine.register_get("subtotal", |si: &mut SaleItem| -> Result<f64, Box<EvalAltResult>> { Ok(si.subtotal) });
|
||||
engine.register_get("service_active_until", |si: &mut SaleItem| -> Result<Option<i64>, Box<EvalAltResult>> { Ok(si.service_active_until) });
|
||||
|
||||
// Builder-style methods for SaleItem
|
||||
engine.register_type_with_name::<SaleItem>("SaleItem")
|
||||
.register_fn("product_id", |item: SaleItem, product_id_i64: i64| -> Result<SaleItem, Box<EvalAltResult>> { Ok(item.product_id(id_from_i64(product_id_i64)?)) })
|
||||
.register_fn("name", |item: SaleItem, name: String| -> Result<SaleItem, Box<EvalAltResult>> { Ok(item.name(name)) })
|
||||
.register_fn("quantity", |item: SaleItem, quantity_i64: i64| -> Result<SaleItem, Box<EvalAltResult>> { Ok(item.quantity(quantity_i64 as i32)) })
|
||||
.register_fn("unit_price", |item: SaleItem, unit_price: f64| -> Result<SaleItem, Box<EvalAltResult>> { Ok(item.unit_price(unit_price)) })
|
||||
.register_fn("subtotal", |item: SaleItem, subtotal: f64| -> Result<SaleItem, Box<EvalAltResult>> { Ok(item.subtotal(subtotal)) })
|
||||
.register_fn("service_active_until", |item: SaleItem, until: Option<i64>| -> Result<SaleItem, Box<EvalAltResult>> { Ok(item.service_active_until(until)) });
|
||||
|
||||
// --- Sale ---
|
||||
engine.register_type_with_name::<Sale>("Sale");
|
||||
engine.register_fn("new_sale", |id_i64: i64, company_id_i64: i64, buyer_name: String, buyer_email: String, total_amount: f64, status: SaleStatus, sale_date: i64| -> Result<Sale, Box<EvalAltResult>> {
|
||||
Ok(Sale::new(id_from_i64(id_i64)?, id_from_i64(company_id_i64)?, buyer_name, buyer_email, total_amount, status, sale_date))
|
||||
});
|
||||
|
||||
// Getters for Sale
|
||||
engine.register_get("id", |s: &mut Sale| -> Result<i64, Box<EvalAltResult>> { Ok(s.get_id() as i64) });
|
||||
engine.register_get("customer_id", |s: &mut Sale| -> Result<i64, Box<EvalAltResult>> { Ok(s.company_id as i64) });
|
||||
engine.register_get("buyer_name", |s: &mut Sale| -> Result<String, Box<EvalAltResult>> { Ok(s.buyer_name.clone()) });
|
||||
engine.register_get("buyer_email", |s: &mut Sale| -> Result<String, Box<EvalAltResult>> { Ok(s.buyer_email.clone()) });
|
||||
engine.register_get("total_amount", |s: &mut Sale| -> Result<f64, Box<EvalAltResult>> { Ok(s.total_amount) });
|
||||
engine.register_get("status", |s: &mut Sale| -> Result<SaleStatus, Box<EvalAltResult>> { Ok(s.status.clone()) });
|
||||
engine.register_get("sale_date", |s: &mut Sale| -> Result<i64, Box<EvalAltResult>> { Ok(s.sale_date) });
|
||||
engine.register_get("items", |s: &mut Sale| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(s.items.iter().cloned().map(Dynamic::from).collect::<rhai::Array>())
|
||||
});
|
||||
engine.register_get("notes", |s: &mut Sale| -> Result<String, Box<EvalAltResult>> { Ok(s.notes.clone()) });
|
||||
engine.register_get("created_at", |s: &mut Sale| -> Result<i64, Box<EvalAltResult>> { Ok(s.base_data.created_at) });
|
||||
engine.register_get("modified_at", |s: &mut Sale| -> Result<i64, Box<EvalAltResult>> { Ok(s.base_data.modified_at) });
|
||||
// engine.register_get("uuid", |s: &mut Sale| -> Result<Option<String>, Box<EvalAltResult>> { Ok(s.base_data().uuid.clone()) }); // UUID not in BaseModelData
|
||||
engine.register_get("comments", |s: &mut Sale| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(s.base_data.comments.iter().map(|&id| Dynamic::from(id as i64)).collect::<rhai::Array>())
|
||||
});
|
||||
|
||||
// Builder-style methods for Sale
|
||||
engine.register_type_with_name::<Sale>("Sale")
|
||||
.register_fn("customer_id", |s: Sale, customer_id_i64: i64| -> Result<Sale, Box<EvalAltResult>> { Ok(s.company_id(id_from_i64(customer_id_i64)?)) })
|
||||
.register_fn("buyer_name", |s: Sale, buyer_name: String| -> Result<Sale, Box<EvalAltResult>> { Ok(s.buyer_name(buyer_name)) })
|
||||
.register_fn("buyer_email", |s: Sale, buyer_email: String| -> Result<Sale, Box<EvalAltResult>> { Ok(s.buyer_email(buyer_email)) })
|
||||
.register_fn("total_amount", |s: Sale, total_amount: f64| -> Result<Sale, Box<EvalAltResult>> { Ok(s.total_amount(total_amount)) })
|
||||
.register_fn("status", |s: Sale, status: SaleStatus| -> Result<Sale, Box<EvalAltResult>> { Ok(s.status(status)) })
|
||||
.register_fn("sale_date", |s: Sale, sale_date: i64| -> Result<Sale, Box<EvalAltResult>> { Ok(s.sale_date(sale_date)) })
|
||||
.register_fn("add_item", |s: Sale, item: SaleItem| -> Result<Sale, Box<EvalAltResult>> { Ok(s.add_item(item)) })
|
||||
.register_fn("items", |s: Sale, items: Vec<SaleItem>| -> Result<Sale, Box<EvalAltResult>> { Ok(s.items(items)) })
|
||||
.register_fn("notes", |s: Sale, notes: String| -> Result<Sale, Box<EvalAltResult>> { Ok(s.notes(notes)) })
|
||||
.register_fn("set_base_id", |s: Sale, id_i64: i64| -> Result<Sale, Box<EvalAltResult>> { Ok(s.set_base_id(id_from_i64(id_i64)?)) })
|
||||
// .register_fn("set_base_uuid", |s: Sale, uuid: Option<String>| -> Result<Sale, Box<EvalAltResult>> { Ok(s.set_base_uuid(uuid)) }) // UUID not in BaseModelData
|
||||
.register_fn("set_base_created_at", |s: Sale, time: i64| -> Result<Sale, Box<EvalAltResult>> { Ok(s.set_base_created_at(time)) })
|
||||
.register_fn("set_base_modified_at", |s: Sale, time: i64| -> Result<Sale, Box<EvalAltResult>> { Ok(s.set_base_modified_at(time)) })
|
||||
.register_fn("add_base_comment", |s: Sale, comment_id_i64: i64| -> Result<Sale, Box<EvalAltResult>> { Ok(s.add_base_comment(id_from_i64(comment_id_i64)?)) })
|
||||
.register_fn("set_base_comments", |s: Sale, comment_ids: Vec<i64>| -> Result<Sale, Box<EvalAltResult>> {
|
||||
let u32_ids = comment_ids.into_iter().map(id_from_i64).collect::<Result<Vec<u32>, _>>()?;
|
||||
Ok(s.set_base_comments(u32_ids))
|
||||
});
|
||||
|
||||
// DB functions for Product
|
||||
let captured_db_for_set_prod = Arc::clone(&db);
|
||||
engine.register_fn("set_product", move |product: Product| -> Result<(), Box<EvalAltResult>> {
|
||||
captured_db_for_set_prod.set(&product).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to set Product (ID: {}): {}", product.get_id(), e).into(), Position::NONE))
|
||||
})
|
||||
});
|
||||
|
||||
let captured_db_for_get_prod = Arc::clone(&db);
|
||||
engine.register_fn("get_product_by_id", move |id_i64: i64| -> Result<Product, Box<EvalAltResult>> {
|
||||
let id_u32 = id_i64 as u32;
|
||||
captured_db_for_get_prod.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Error getting Product (ID: {}): {}", id_u32, e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Product with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
|
||||
// DB functions for Sale
|
||||
let captured_db_for_set_sale = Arc::clone(&db);
|
||||
engine.register_fn("set_sale", move |sale: Sale| -> Result<(), Box<EvalAltResult>> {
|
||||
captured_db_for_set_sale.set(&sale).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to set Sale (ID: {}): {}", sale.get_id(), e).into(), Position::NONE))
|
||||
})
|
||||
});
|
||||
|
||||
let captured_db_for_get_sale = Arc::clone(&db);
|
||||
engine.register_fn("get_sale_by_id", move |id_i64: i64| -> Result<Sale, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64(id_i64)?;
|
||||
captured_db_for_get_sale.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Error getting Sale (ID: {}): {}", id_u32, e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Sale with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
|
||||
// Mock DB functions for Shareholder
|
||||
let captured_db_for_set_sh = Arc::clone(&db);
|
||||
engine.register_fn("set_shareholder", move |shareholder: Shareholder| -> Result<(), Box<EvalAltResult>> {
|
||||
captured_db_for_set_sh.set(&shareholder).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to set Shareholder (ID: {}): {}", shareholder.get_id(), e).into(), Position::NONE))
|
||||
})
|
||||
});
|
||||
|
||||
let captured_db_for_get_sh = Arc::clone(&db);
|
||||
engine.register_fn("get_shareholder_by_id", move |id_i64: i64| -> Result<Shareholder, Box<EvalAltResult>> {
|
||||
let id_u32 = id_i64 as u32;
|
||||
captured_db_for_get_sh.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Error getting Shareholder (ID: {}): {}", id_u32, e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Shareholder with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
|
||||
// Mock DB functions for Company
|
||||
let captured_db_for_set = Arc::clone(&db);
|
||||
engine.register_fn("set_company", move |company: Company| -> Result<(), Box<EvalAltResult>> {
|
||||
captured_db_for_set.set(&company).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to set Company (ID: {}): {}", company.get_id(), e).into(), Position::NONE))
|
||||
})
|
||||
});
|
||||
|
||||
let captured_db_for_get = Arc::clone(&db);
|
||||
engine.register_fn("get_company_by_id", move |id_i64: i64| -> Result<Company, Box<EvalAltResult>> {
|
||||
let id_u32 = id_i64 as u32; // Assuming direct conversion is fine, or use a helper like in flow
|
||||
captured_db_for_get.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Error getting Company (ID: {}): {}", id_u32, e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Company with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
|
||||
engine.register_global_module(module.into());
|
||||
}
|
203
heromodels/src/models/biz/sale.rs
Normal file
203
heromodels/src/models/biz/sale.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::{BaseModelData, Model};
|
||||
|
||||
/// Represents the status of a sale.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum SaleStatus {
|
||||
Pending,
|
||||
Completed,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl Default for SaleStatus {
|
||||
fn default() -> Self {
|
||||
SaleStatus::Pending
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an individual item within a Sale.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SaleItem {
|
||||
pub product_id: u32,
|
||||
pub name: String, // Denormalized product name at time of sale
|
||||
pub quantity: i32,
|
||||
pub unit_price: f64, // Price per unit at time of sale
|
||||
pub subtotal: f64,
|
||||
pub service_active_until: Option<i64>, // Optional: For services, date until this specific purchased instance is active
|
||||
}
|
||||
|
||||
impl SaleItem {
|
||||
/// Creates a new `SaleItem`.
|
||||
pub fn new(product_id: u32, name: String, quantity: i32, unit_price: f64, subtotal: f64) -> Self {
|
||||
SaleItem {
|
||||
product_id,
|
||||
name,
|
||||
quantity,
|
||||
unit_price,
|
||||
subtotal,
|
||||
service_active_until: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods
|
||||
pub fn product_id(mut self, product_id: u32) -> Self {
|
||||
self.product_id = product_id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn quantity(mut self, quantity: i32) -> Self {
|
||||
self.quantity = quantity;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unit_price(mut self, unit_price: f64) -> Self {
|
||||
self.unit_price = unit_price;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn subtotal(mut self, subtotal: f64) -> Self {
|
||||
self.subtotal = subtotal;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn service_active_until(mut self, service_active_until: Option<i64>) -> Self {
|
||||
self.service_active_until = service_active_until;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a sale of products or services.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Sale {
|
||||
pub base_data: BaseModelData,
|
||||
pub company_id: u32,
|
||||
pub buyer_name: String,
|
||||
pub buyer_email: String,
|
||||
pub total_amount: f64,
|
||||
pub status: SaleStatus,
|
||||
pub sale_date: i64,
|
||||
pub items: Vec<SaleItem>,
|
||||
pub notes: String,
|
||||
}
|
||||
|
||||
impl Model for Sale {
|
||||
fn db_prefix() -> &'static str {
|
||||
"sale"
|
||||
}
|
||||
|
||||
fn get_id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
fn base_data_mut(&mut self) -> &mut BaseModelData {
|
||||
&mut self.base_data
|
||||
}
|
||||
}
|
||||
|
||||
impl Sale {
|
||||
/// Creates a new `Sale`.
|
||||
pub fn new(
|
||||
id: u32,
|
||||
company_id: u32,
|
||||
buyer_name: String,
|
||||
buyer_email: String,
|
||||
total_amount: f64,
|
||||
status: SaleStatus,
|
||||
sale_date: i64,
|
||||
) -> Self {
|
||||
Sale {
|
||||
base_data: BaseModelData::new(id),
|
||||
company_id,
|
||||
buyer_name,
|
||||
buyer_email,
|
||||
total_amount,
|
||||
status,
|
||||
sale_date,
|
||||
items: Vec::new(),
|
||||
notes: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods for Sale
|
||||
pub fn company_id(mut self, company_id: u32) -> Self {
|
||||
self.company_id = company_id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn buyer_name(mut self, buyer_name: String) -> Self {
|
||||
self.buyer_name = buyer_name;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn buyer_email(mut self, buyer_email: String) -> Self {
|
||||
self.buyer_email = buyer_email;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn total_amount(mut self, total_amount: f64) -> Self {
|
||||
self.total_amount = total_amount;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn status(mut self, status: SaleStatus) -> Self {
|
||||
self.status = status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sale_date(mut self, sale_date: i64) -> Self {
|
||||
self.sale_date = sale_date;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn items(mut self, items: Vec<SaleItem>) -> Self {
|
||||
self.items = items;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_item(mut self, item: SaleItem) -> Self {
|
||||
self.items.push(item);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn notes(mut self, notes: String) -> Self {
|
||||
self.notes = notes;
|
||||
self
|
||||
}
|
||||
|
||||
// Builder methods for BaseModelData fields, prefixed with base_
|
||||
pub fn set_base_id(mut self, id: u32) -> Self {
|
||||
self.base_data.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
// UUID is not part of BaseModelData directly in heromodels_core
|
||||
// pub fn set_base_uuid(mut self, uuid: Option<String>) -> Self {
|
||||
// self.base_data.uuid = uuid; // Assuming uuid field exists if needed elsewhere
|
||||
// self
|
||||
// }
|
||||
|
||||
pub fn set_base_created_at(mut self, created_at: i64) -> Self {
|
||||
self.base_data.created_at = created_at;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_base_modified_at(mut self, modified_at: i64) -> Self {
|
||||
self.base_data.modified_at = modified_at;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_base_comment(mut self, comment_id: u32) -> Self {
|
||||
self.base_data.comments.push(comment_id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_base_comments(mut self, comments: Vec<u32>) -> Self {
|
||||
self.base_data.comments = comments;
|
||||
self
|
||||
}
|
||||
}
|
102
heromodels/src/models/biz/shareholder.rs
Normal file
102
heromodels/src/models/biz/shareholder.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::{BaseModelData, Model};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ShareholderType {
|
||||
Individual,
|
||||
Corporate,
|
||||
}
|
||||
|
||||
impl Default for ShareholderType {
|
||||
fn default() -> Self {
|
||||
ShareholderType::Individual
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Shareholder {
|
||||
pub base_data: BaseModelData,
|
||||
pub company_id: u32,
|
||||
pub user_id: u32, // Or other entity ID
|
||||
pub name: String,
|
||||
pub shares: f64,
|
||||
pub percentage: f64,
|
||||
pub type_: ShareholderType,
|
||||
pub since: i64, // Timestamp
|
||||
}
|
||||
|
||||
impl Shareholder {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
company_id: 0, // Default, to be set by builder
|
||||
user_id: 0, // Default, to be set by builder
|
||||
name: String::new(), // Default
|
||||
shares: 0.0, // Default
|
||||
percentage: 0.0, // Default
|
||||
type_: ShareholderType::default(), // Uses ShareholderType's Default impl
|
||||
since: 0, // Default timestamp, to be set by builder
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods
|
||||
pub fn company_id(mut self, company_id: u32) -> Self {
|
||||
self.company_id = company_id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn user_id(mut self, user_id: u32) -> Self {
|
||||
self.user_id = user_id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn shares(mut self, shares: f64) -> Self {
|
||||
self.shares = shares;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn percentage(mut self, percentage: f64) -> Self {
|
||||
self.percentage = percentage;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn type_(mut self, type_: ShareholderType) -> Self {
|
||||
self.type_ = type_;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn since(mut self, since: i64) -> Self {
|
||||
self.since = since;
|
||||
self
|
||||
}
|
||||
|
||||
// Base data setters if needed for Rhai or specific scenarios
|
||||
pub fn set_base_created_at(mut self, created_at: i64) -> Self {
|
||||
self.base_data.created_at = created_at;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_base_modified_at(mut self, modified_at: i64) -> Self {
|
||||
self.base_data.modified_at = modified_at;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Model for Shareholder {
|
||||
fn db_prefix() -> &'static str {
|
||||
"shareholder"
|
||||
}
|
||||
|
||||
fn get_id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
fn base_data_mut(&mut self) -> &mut BaseModelData {
|
||||
&mut self.base_data
|
||||
}
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai_autobind_macros::rhai_model_export;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents the status of an attendee for an event
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
@@ -15,19 +15,19 @@ pub enum AttendanceStatus {
|
||||
}
|
||||
|
||||
/// Represents an attendee of an event
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||
pub struct Attendee {
|
||||
/// ID of the user attending
|
||||
// Assuming user_id might be queryable
|
||||
pub user_id: String, // Using String for user_id similar to potential external IDs
|
||||
pub contact_id: u32,
|
||||
/// Attendance status of the user for the event
|
||||
pub status: AttendanceStatus,
|
||||
}
|
||||
|
||||
impl Attendee {
|
||||
pub fn new(user_id: String) -> Self {
|
||||
pub fn new(contact_id: u32) -> Self {
|
||||
Self {
|
||||
user_id,
|
||||
contact_id,
|
||||
status: AttendanceStatus::NoResponse,
|
||||
}
|
||||
}
|
||||
@@ -39,11 +39,11 @@ impl Attendee {
|
||||
}
|
||||
|
||||
/// Represents an event in a calendar
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||
pub struct Event {
|
||||
/// Unique identifier for the event (e.g., could be a UUID string or u32 if internally managed)
|
||||
// Events might be looked up by their ID
|
||||
pub id: String,
|
||||
/// Base model data
|
||||
#[serde(flatten)]
|
||||
pub base_data: BaseModelData,
|
||||
/// Title of the event
|
||||
pub title: String,
|
||||
/// Optional description of the event
|
||||
@@ -60,18 +60,24 @@ pub struct Event {
|
||||
|
||||
impl Event {
|
||||
/// Creates a new event
|
||||
pub fn new(id: String, title: impl ToString, start_time: DateTime<Utc>, end_time: DateTime<Utc>) -> Self {
|
||||
pub fn new(id: i64) -> Self {
|
||||
Self {
|
||||
id,
|
||||
title: title.to_string(),
|
||||
base_data: BaseModelData::new(id as u32),
|
||||
title: String::new(),
|
||||
description: None,
|
||||
start_time,
|
||||
end_time,
|
||||
start_time: Utc::now(),
|
||||
end_time: Utc::now(),
|
||||
attendees: Vec::new(),
|
||||
location: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the title for the event
|
||||
pub fn title(mut self, title: impl ToString) -> Self {
|
||||
self.title = title.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the description for the event
|
||||
pub fn description(mut self, description: impl ToString) -> Self {
|
||||
self.description = Some(description.to_string());
|
||||
@@ -86,22 +92,22 @@ impl Event {
|
||||
|
||||
/// Adds an attendee to the event
|
||||
pub fn add_attendee(mut self, attendee: Attendee) -> Self {
|
||||
// Prevent duplicate attendees by user_id
|
||||
if !self.attendees.iter().any(|a| a.user_id == attendee.user_id) {
|
||||
// Prevent duplicate attendees by contact_id
|
||||
if !self.attendees.iter().any(|a| a.contact_id == attendee.contact_id) {
|
||||
self.attendees.push(attendee);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Removes an attendee from the event by user_id
|
||||
pub fn remove_attendee(mut self, user_id: &str) -> Self {
|
||||
self.attendees.retain(|a| a.user_id != user_id);
|
||||
pub fn remove_attendee(mut self, contact_id: u32) -> Self {
|
||||
self.attendees.retain(|a| a.contact_id != contact_id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Updates the status of an existing attendee
|
||||
pub fn update_attendee_status(mut self, user_id: &str, status: AttendanceStatus) -> Self {
|
||||
if let Some(attendee) = self.attendees.iter_mut().find(|a| a.user_id == user_id) {
|
||||
pub fn update_attendee_status(mut self, contact_id: u32, status: AttendanceStatus) -> Self {
|
||||
if let Some(attendee) = self.attendees.iter_mut().find(|a| a.contact_id == contact_id) {
|
||||
attendee.status = status;
|
||||
}
|
||||
self
|
||||
@@ -120,11 +126,14 @@ impl Event {
|
||||
}
|
||||
|
||||
/// Represents a calendar with events
|
||||
#[rhai_model_export(db_type = "std::sync::Arc<crate::db::hero::OurDB>")]
|
||||
#[rhai_model_export(
|
||||
db_type = "std::sync::Arc<crate::db::hero::OurDB>",
|
||||
)]
|
||||
#[model]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||
pub struct Calendar {
|
||||
/// Base model data
|
||||
#[serde(flatten)]
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// Name of the calendar
|
||||
@@ -135,20 +144,26 @@ pub struct Calendar {
|
||||
|
||||
/// List of events in the calendar
|
||||
// For now, events are embedded. If they become separate models, this would be Vec<[IDType]>.
|
||||
pub events: Vec<Event>,
|
||||
pub events: Vec<i64>,
|
||||
}
|
||||
|
||||
impl Calendar {
|
||||
/// Creates a new calendar
|
||||
pub fn new(id: u32, name: impl ToString) -> Self {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
name: name.to_string(),
|
||||
base_data: BaseModelData::new(id as u32),
|
||||
name: String::new(),
|
||||
description: None,
|
||||
events: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the name for the calendar
|
||||
pub fn name(mut self, name: impl ToString) -> Self {
|
||||
self.name = name.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the description for the calendar
|
||||
pub fn description(mut self, description: impl ToString) -> Self {
|
||||
self.description = Some(description.to_string());
|
||||
@@ -156,29 +171,17 @@ impl Calendar {
|
||||
}
|
||||
|
||||
/// Adds an event to the calendar
|
||||
pub fn add_event(mut self, event: Event) -> Self {
|
||||
pub fn add_event(mut self, event_id: i64) -> Self {
|
||||
// Prevent duplicate events by id
|
||||
if !self.events.iter().any(|e| e.id == event.id) {
|
||||
self.events.push(event);
|
||||
if !self.events.iter().any(|e_id| *e_id == event_id) {
|
||||
self.events.push(event_id);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Removes an event from the calendar by its ID
|
||||
pub fn remove_event(mut self, event_id: &str) -> Self {
|
||||
self.events.retain(|event| event.id != event_id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Finds an event by its ID and allows modification
|
||||
pub fn update_event<F>(mut self, event_id: &str, update_fn: F) -> Self
|
||||
where
|
||||
F: FnOnce(Event) -> Event,
|
||||
{
|
||||
if let Some(index) = self.events.iter().position(|e| e.id == event_id) {
|
||||
let event = self.events.remove(index);
|
||||
self.events.insert(index, update_fn(event));
|
||||
}
|
||||
pub fn remove_event(mut self, event_id_to_remove: i64) -> Self {
|
||||
self.events.retain(|&event_id_in_vec| event_id_in_vec != event_id_to_remove);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
// Export calendar module
|
||||
pub mod calendar;
|
||||
pub mod rhai;
|
||||
|
||||
// Re-export Calendar, Event, Attendee, and AttendanceStatus from the inner calendar module (calendar.rs) within src/models/calendar/mod.rs
|
||||
pub use self::calendar::{Calendar, Event, Attendee, AttendanceStatus};
|
||||
pub use rhai::register_rhai_engine_functions as register_calendar_rhai_module;
|
||||
|
79
heromodels/src/models/calendar/rhai.rs
Normal file
79
heromodels/src/models/calendar/rhai.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use rhai::{Engine, EvalAltResult, NativeCallContext};
|
||||
use std::sync::Arc;
|
||||
|
||||
use heromodels_core::BaseModelData;
|
||||
use crate::db::hero::OurDB;
|
||||
use super::calendar::{Calendar, Event, Attendee, AttendanceStatus};
|
||||
use adapter_macros::{adapt_rhai_i64_input_fn, adapt_rhai_i64_input_method};
|
||||
use rhai_wrapper::wrap_vec_return;
|
||||
|
||||
// Helper function for get_all_calendars registration
|
||||
fn get_all_calendars_helper(_db: Arc<OurDB>) -> Vec<Calendar> {
|
||||
// In a real implementation, this would retrieve all calendars from the database
|
||||
vec![Calendar::new(1 as u32), Calendar::new(2 as u32)]
|
||||
}
|
||||
|
||||
pub fn register_rhai_engine_functions(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
engine.register_fn("new_calendar", adapt_rhai_i64_input_fn!(Calendar::new, u32));
|
||||
engine.register_fn("name", move |calendar: Calendar, name: String| Calendar::name(calendar, name));
|
||||
engine.register_fn("description", move |calendar: Calendar, description: String| Calendar::description(calendar, description));
|
||||
engine.register_fn("add_event", Calendar::add_event); // Corrected: expects i64, Rhai provides i64
|
||||
|
||||
engine.register_fn("new_event", Event::new); // Corrected: expects i64, Rhai provides i64
|
||||
engine.register_fn("title", move |event: Event, title: String| Event::title(event, title));
|
||||
engine.register_fn("description", move |event: Event, description: String| Event::description(event, description));
|
||||
engine.register_fn("add_attendee", move |event: Event, attendee: Attendee| Event::add_attendee(event, attendee));
|
||||
engine.register_fn("remove_attendee", adapt_rhai_i64_input_method!(Event, remove_attendee, u32));
|
||||
engine.register_fn("update_attendee_status", move |context: NativeCallContext, event: Event, contact_id_i64: i64, status: AttendanceStatus| -> Result<Event, Box<EvalAltResult>> {
|
||||
let contact_id_u32: u32 = contact_id_i64.try_into().map_err(|_e| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Conversion error for contact_id in Event::update_attendee_status from i64 to u32"),
|
||||
context.position(),
|
||||
))
|
||||
})?;
|
||||
Ok(event.update_attendee_status(contact_id_u32, status))
|
||||
});
|
||||
|
||||
engine.register_fn("new_attendee", adapt_rhai_i64_input_fn!(Attendee::new, u32));
|
||||
|
||||
// Register a function to get the database instance
|
||||
engine.register_fn("get_db", move || db.clone());
|
||||
|
||||
// Register getters for Calendar
|
||||
engine.register_get("id", |c: &mut Calendar| -> Result<i64, Box<EvalAltResult>> { Ok(c.base_data.id as i64) });
|
||||
engine.register_get("name", |c: &mut Calendar| -> Result<String, Box<EvalAltResult>> {
|
||||
// println!("Rhai attempting to get Calendar.name: {}", c.name); // Debug print
|
||||
Ok(c.name.clone())
|
||||
});
|
||||
engine.register_get("description", |c: &mut Calendar| -> Result<Option<String>, Box<EvalAltResult>> { Ok(c.description.clone()) });
|
||||
|
||||
// Register getter for Calendar.base_data
|
||||
engine.register_get("base_data", |c: &mut Calendar| -> Result<BaseModelData, Box<EvalAltResult>> { Ok(c.base_data.clone()) });
|
||||
|
||||
// Register getters for BaseModelData
|
||||
engine.register_get("id", |bmd: &mut BaseModelData| -> Result<i64, Box<EvalAltResult>> { Ok(bmd.id.into()) });
|
||||
|
||||
// Mock database interaction functions from the example - these would typically interact with the `db` Arc
|
||||
engine.register_fn("set_calendar", |_db: Arc<OurDB>, calendar: Calendar| {
|
||||
println!("Mock save: Calendar saved: {}", calendar.name);
|
||||
});
|
||||
|
||||
engine.register_fn("get_calendar_by_id", |_db: Arc<OurDB>, id_i64: i64| -> Calendar {
|
||||
Calendar::new(id_i64 as u32)
|
||||
});
|
||||
|
||||
engine.register_fn("calendar_exists", |_db: Arc<OurDB>, id_i64: i64| -> bool {
|
||||
id_i64 == 1 || id_i64 == 2 // Mock check
|
||||
});
|
||||
|
||||
engine.register_fn("get_all_calendars", wrap_vec_return!(get_all_calendars_helper, Arc<OurDB> => Calendar));
|
||||
|
||||
engine.register_fn("delete_calendar_by_id", |_db: Arc<OurDB>, id_i64: i64| {
|
||||
println!("Mock delete: Calendar deleted with ID: {}", id_i64);
|
||||
});
|
||||
|
||||
// Getters for Event
|
||||
engine.register_get("id", |e: &mut Event| -> Result<i64, Box<EvalAltResult>> { Ok(e.base_data.id as i64) });
|
||||
engine.register_get("title", |e: &mut Event| -> Result<String, Box<EvalAltResult>> { Ok(e.title.clone()) });
|
||||
// Add more getters for Event fields as needed
|
||||
}
|
@@ -1,13 +1,14 @@
|
||||
// heromodels/src/models/finance/account.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
|
||||
use super::asset::Asset;
|
||||
|
||||
/// Account represents a financial account owned by a user
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[model] // Has base.Base in V spec
|
||||
pub struct Account {
|
||||
pub base_data: BaseModelData,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
// heromodels/src/models/finance/asset.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
|
||||
@@ -20,7 +21,7 @@ impl Default for AssetType {
|
||||
}
|
||||
|
||||
/// Asset represents a digital asset or token
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[model] // Has base.Base in V spec
|
||||
pub struct Asset {
|
||||
pub base_data: BaseModelData,
|
||||
|
@@ -1,9 +1,10 @@
|
||||
// heromodels/src/models/finance/marketplace.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
|
||||
use super::asset::AssetType;
|
||||
|
||||
@@ -52,7 +53,7 @@ impl Default for BidStatus {
|
||||
}
|
||||
|
||||
/// Bid represents a bid on an auction listing
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
pub struct Bid {
|
||||
pub listing_id: String, // ID of the listing this bid belongs to
|
||||
pub bidder_id: u32, // ID of the user who placed the bid
|
||||
@@ -88,7 +89,7 @@ impl Bid {
|
||||
}
|
||||
|
||||
/// Listing represents a marketplace listing for an asset
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[model] // Has base.Base in V spec
|
||||
pub struct Listing {
|
||||
pub base_data: BaseModelData,
|
||||
|
@@ -4,7 +4,9 @@
|
||||
pub mod account;
|
||||
pub mod asset;
|
||||
pub mod marketplace;
|
||||
pub mod rhai;
|
||||
|
||||
// Re-export main models for easier access
|
||||
pub use self::account::Account;
|
||||
pub use self::asset::{Asset, AssetType};
|
||||
pub use self::marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus};
|
||||
|
317
heromodels/src/models/finance/rhai.rs
Normal file
317
heromodels/src/models/finance/rhai.rs
Normal file
@@ -0,0 +1,317 @@
|
||||
use rhai::{Engine, Array, ImmutableString, INT, EvalAltResult};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use chrono::Utc;
|
||||
|
||||
use adapter_macros::register_rhai_enum_accessors;
|
||||
use adapter_macros::register_rhai_datetime_accessors;
|
||||
use adapter_macros::register_rhai_vec_string_accessors;
|
||||
use adapter_macros::rhai_timestamp_helpers;
|
||||
|
||||
use crate::models::finance::account::Account;
|
||||
use crate::models::finance::asset::{Asset, AssetType};
|
||||
use crate::models::finance::marketplace::{Listing, Bid, ListingStatus, ListingType, BidStatus};
|
||||
|
||||
// --- Enum to String & String to Enum Helper Functions (domain-specific) ---
|
||||
// These remain here as they are specific to the finance models' enums.
|
||||
|
||||
fn asset_type_to_string(asset_type: &AssetType) -> ImmutableString {
|
||||
format!("{:?}", asset_type).into()
|
||||
}
|
||||
fn string_to_asset_type(s: &str) -> Result<AssetType, Box<EvalAltResult>> {
|
||||
match s {
|
||||
"Erc20" => Ok(AssetType::Erc20),
|
||||
"Erc721" => Ok(AssetType::Erc721),
|
||||
"Erc1155" => Ok(AssetType::Erc1155),
|
||||
"Native" => Ok(AssetType::Native),
|
||||
_ => Err(format!("Invalid AssetType string: {}", s).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn listing_status_to_string(status: &ListingStatus) -> ImmutableString {
|
||||
format!("{:?}", status).into()
|
||||
}
|
||||
fn string_to_listing_status(s: &str) -> Result<ListingStatus, Box<EvalAltResult>> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"active" => Ok(ListingStatus::Active),
|
||||
"sold" => Ok(ListingStatus::Sold),
|
||||
"cancelled" => Ok(ListingStatus::Cancelled),
|
||||
"expired" => Ok(ListingStatus::Expired),
|
||||
_ => Err(format!("Invalid ListingStatus string: {}", s).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn listing_type_to_string(lt: &ListingType) -> ImmutableString {
|
||||
format!("{:?}", lt).into()
|
||||
}
|
||||
fn string_to_listing_type(s: &str) -> Result<ListingType, Box<EvalAltResult>> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"fixedprice" => Ok(ListingType::FixedPrice),
|
||||
"auction" => Ok(ListingType::Auction),
|
||||
"exchange" => Ok(ListingType::Exchange),
|
||||
_ => Err(format!("Invalid ListingType string: {}", s).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn bid_status_to_string(status: &BidStatus) -> ImmutableString {
|
||||
format!("{:?}", status).into()
|
||||
}
|
||||
fn string_to_bid_status(s: &str) -> Result<BidStatus, Box<EvalAltResult>> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"active" => Ok(BidStatus::Active),
|
||||
"accepted" => Ok(BidStatus::Accepted),
|
||||
"rejected" => Ok(BidStatus::Rejected),
|
||||
"cancelled" => Ok(BidStatus::Cancelled),
|
||||
_ => Err(format!("Invalid BidStatus string: {}", s).into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn register_rhai_engine_functions(
|
||||
engine: &mut Engine,
|
||||
db_accounts: Arc<Mutex<HashMap<u32, Account>>>,
|
||||
db_assets: Arc<Mutex<HashMap<u32, Asset>>>,
|
||||
db_listings: Arc<Mutex<HashMap<u32, Listing>>>,
|
||||
) {
|
||||
// --- Account model ---
|
||||
engine.build_type::<Account>()
|
||||
.register_fn("new_account",
|
||||
|id_rhai: INT, name_rhai: ImmutableString, user_id_rhai: INT, desc_rhai: ImmutableString, ledger_rhai: ImmutableString, addr_rhai: ImmutableString, pubkey_rhai: ImmutableString| -> Account {
|
||||
Account::new(id_rhai as u32, name_rhai, user_id_rhai as u32, desc_rhai, ledger_rhai, addr_rhai, pubkey_rhai)
|
||||
}
|
||||
)
|
||||
.register_get_set("name",
|
||||
|obj: &mut Account| -> ImmutableString { obj.name.clone().into() },
|
||||
|obj: &mut Account, val: ImmutableString| obj.name = val.to_string()
|
||||
)
|
||||
.register_get_set("user_id",
|
||||
|obj: &mut Account| obj.user_id as INT,
|
||||
|obj: &mut Account, val: INT| obj.user_id = val as u32
|
||||
)
|
||||
.register_get_set("description",
|
||||
|obj: &mut Account| -> ImmutableString { obj.description.clone().into() },
|
||||
|obj: &mut Account, val: ImmutableString| obj.description = val.to_string()
|
||||
)
|
||||
.register_get_set("ledger",
|
||||
|obj: &mut Account| -> ImmutableString { obj.ledger.clone().into() },
|
||||
|obj: &mut Account, val: ImmutableString| obj.ledger = val.to_string()
|
||||
)
|
||||
.register_get_set("address",
|
||||
|obj: &mut Account| -> ImmutableString { obj.address.clone().into() },
|
||||
|obj: &mut Account, val: ImmutableString| obj.address = val.to_string()
|
||||
)
|
||||
.register_get_set("pubkey",
|
||||
|obj: &mut Account| -> ImmutableString { obj.pubkey.clone().into() },
|
||||
|obj: &mut Account, val: ImmutableString| obj.pubkey = val.to_string()
|
||||
)
|
||||
.register_get("created_at_ts", |obj: &mut Account| obj.base_data.created_at);
|
||||
engine.register_get("assets_list", |acc: &mut Account| -> Array {
|
||||
acc.assets.iter().cloned().map(rhai::Dynamic::from).collect()
|
||||
});
|
||||
|
||||
// --- Asset model ---
|
||||
engine.build_type::<Asset>()
|
||||
.register_fn("new_asset",
|
||||
|id_rhai: INT, name: ImmutableString, description: ImmutableString, amount: f64, address: ImmutableString, asset_type_str_rhai: ImmutableString, decimals_rhai: INT| -> Result<Asset, Box<EvalAltResult>> {
|
||||
let asset_type = self::string_to_asset_type(asset_type_str_rhai.as_str())?;
|
||||
Ok(Asset::new(id_rhai as u32, name, description, amount, address, asset_type, decimals_rhai as u8))
|
||||
}
|
||||
)
|
||||
.register_get("id", |asset: &mut Asset| asset.base_data.id as INT)
|
||||
.register_get_set("name",
|
||||
|asset: &mut Asset| -> ImmutableString { asset.name.clone().into() },
|
||||
|asset: &mut Asset, val: ImmutableString| asset.name = val.to_string()
|
||||
)
|
||||
.register_get_set("description",
|
||||
|asset: &mut Asset| -> ImmutableString { asset.description.clone().into() },
|
||||
|asset: &mut Asset, val: ImmutableString| asset.description = val.to_string()
|
||||
)
|
||||
.register_get_set("amount", |asset: &mut Asset| asset.amount, |asset: &mut Asset, amount_val: f64| asset.amount = amount_val)
|
||||
.register_get_set("address",
|
||||
|asset: &mut Asset| -> ImmutableString { asset.address.clone().into() },
|
||||
|asset: &mut Asset, val: ImmutableString| asset.address = val.to_string()
|
||||
)
|
||||
.register_get("decimals", |asset: &mut Asset| asset.decimals as INT)
|
||||
.register_get("created_at_ts", |obj: &mut Asset| obj.base_data.created_at);
|
||||
register_rhai_enum_accessors!(engine, Asset, asset_type, "asset_type_str", self::asset_type_to_string, self::string_to_asset_type);
|
||||
|
||||
// --- Bid model ---
|
||||
engine.register_type_with_name::<Bid>("Bid")
|
||||
.register_fn("new_bid",
|
||||
|listing_id_rhai: ImmutableString, bidder_id_rhai: INT, amount_rhai: f64, currency_rhai: ImmutableString| -> Bid {
|
||||
Bid::new(listing_id_rhai, bidder_id_rhai as u32, amount_rhai, currency_rhai)
|
||||
}
|
||||
)
|
||||
.register_get_set("listing_id",
|
||||
|bid: &mut Bid| -> ImmutableString { bid.listing_id.clone().into() },
|
||||
|bid: &mut Bid, val: ImmutableString| bid.listing_id = val.to_string()
|
||||
)
|
||||
.register_get_set("bidder_id", |bid: &mut Bid| bid.bidder_id as INT, |bid: &mut Bid, val: INT| bid.bidder_id = val as u32)
|
||||
.register_get_set("amount", |bid: &mut Bid| bid.amount, |bid: &mut Bid, val: f64| bid.amount = val)
|
||||
.register_get_set("currency",
|
||||
|bid: &mut Bid| -> ImmutableString { bid.currency.clone().into() },
|
||||
|bid: &mut Bid, val: ImmutableString| bid.currency = val.to_string()
|
||||
);
|
||||
register_rhai_enum_accessors!(engine, Bid, status, "status_str", self::bid_status_to_string, self::string_to_bid_status);
|
||||
register_rhai_datetime_accessors!(engine, Bid, created_at, "created_at_ts", _required);
|
||||
engine.register_fn("update_bid_status_script",
|
||||
|bid: Bid, status_str_rhai: ImmutableString| -> Result<Bid, Box<EvalAltResult>> {
|
||||
let status = self::string_to_bid_status(status_str_rhai.as_str())?;
|
||||
let updated_bid = bid.update_status(status);
|
||||
Ok(updated_bid)
|
||||
}
|
||||
);
|
||||
|
||||
// --- Listing --- (id is u32)
|
||||
engine.register_type_with_name::<Listing>("Listing")
|
||||
.register_fn("new_listing",
|
||||
|id_rhai: INT, title_rhai: ImmutableString, description_rhai: ImmutableString,
|
||||
asset_id_rhai: ImmutableString, asset_type_str_rhai: ImmutableString, seller_id_rhai: ImmutableString,
|
||||
price_rhai: f64, currency_rhai: ImmutableString, listing_type_str_rhai: ImmutableString,
|
||||
expires_at_ts_opt_rhai: Option<INT>, tags_dyn_rhai: Array, image_url_opt_rhai: Option<ImmutableString>|
|
||||
-> Result<Listing, Box<EvalAltResult>> {
|
||||
|
||||
let asset_type = self::string_to_asset_type(asset_type_str_rhai.as_str())?;
|
||||
let listing_type = self::string_to_listing_type(listing_type_str_rhai.as_str())?;
|
||||
let expires_at = rhai_timestamp_helpers::option_rhai_timestamp_to_datetime(expires_at_ts_opt_rhai)?;
|
||||
|
||||
let tags = tags_dyn_rhai.into_iter().map(|d| d.into_string().unwrap_or_default()).collect();
|
||||
let image_url = image_url_opt_rhai.map(|s| s.to_string());
|
||||
|
||||
Ok(Listing::new(
|
||||
id_rhai as u32, title_rhai, description_rhai, asset_id_rhai,
|
||||
asset_type, seller_id_rhai, price_rhai, currency_rhai, listing_type,
|
||||
expires_at, tags, image_url
|
||||
))
|
||||
}
|
||||
)
|
||||
.register_get("id", |l: &mut Listing| l.base_data.id as INT)
|
||||
.register_get_set("title",
|
||||
|l: &mut Listing| -> ImmutableString { l.title.clone().into() },
|
||||
|l: &mut Listing, v: ImmutableString| l.title = v.to_string()
|
||||
)
|
||||
.register_get_set("description",
|
||||
|l: &mut Listing| -> ImmutableString { l.description.clone().into() },
|
||||
|l: &mut Listing, v: ImmutableString| l.description = v.to_string()
|
||||
)
|
||||
.register_get_set("asset_id",
|
||||
|l: &mut Listing| -> ImmutableString { l.asset_id.clone().into() },
|
||||
|l: &mut Listing, v: ImmutableString| l.asset_id = v.to_string()
|
||||
)
|
||||
.register_get_set("seller_id",
|
||||
|l: &mut Listing| -> ImmutableString { l.seller_id.clone().into() },
|
||||
|l: &mut Listing, v: ImmutableString| l.seller_id = v.to_string()
|
||||
)
|
||||
.register_get_set("price", |l: &mut Listing| l.price, |l: &mut Listing, v: f64| l.price = v)
|
||||
.register_get_set("currency",
|
||||
|l: &mut Listing| -> ImmutableString { l.currency.clone().into() },
|
||||
|l: &mut Listing, v: ImmutableString| l.currency = v.to_string()
|
||||
)
|
||||
.register_get_set("buyer_id",
|
||||
|l: &mut Listing| -> Option<ImmutableString> { l.buyer_id.clone().map(ImmutableString::from) },
|
||||
|l: &mut Listing, v_opt: Option<ImmutableString>| l.buyer_id = v_opt.map(|s| s.to_string())
|
||||
)
|
||||
.register_get_set("sale_price",
|
||||
|l: &mut Listing| -> Option<f64> { l.sale_price },
|
||||
|l: &mut Listing, v_opt: Option<f64>| l.sale_price = v_opt
|
||||
)
|
||||
.register_get_set("image_url",
|
||||
|l: &mut Listing| -> Option<ImmutableString> { l.image_url.clone().map(ImmutableString::from) },
|
||||
|l: &mut Listing, v: Option<ImmutableString>| l.image_url = v.map(|s| s.to_string())
|
||||
)
|
||||
.register_get("created_at_ts", |obj: &mut Listing| obj.base_data.created_at);
|
||||
|
||||
register_rhai_enum_accessors!(engine, Listing, listing_type, "listing_type_str", self::listing_type_to_string, self::string_to_listing_type);
|
||||
register_rhai_enum_accessors!(engine, Listing, status, "status_str", self::listing_status_to_string, self::string_to_listing_status);
|
||||
register_rhai_enum_accessors!(engine, Listing, asset_type, "asset_type_str_listing", self::asset_type_to_string, self::string_to_asset_type);
|
||||
|
||||
register_rhai_datetime_accessors!(engine, Listing, expires_at, "expires_at_ts_opt");
|
||||
register_rhai_datetime_accessors!(engine, Listing, sold_at, "sold_at_ts_opt");
|
||||
|
||||
register_rhai_vec_string_accessors!(engine, Listing, tags, "tags_cloned");
|
||||
|
||||
engine.register_fn("add_listing_bid",
|
||||
|listing: Listing, bid: Bid| -> Result<Listing, Box<EvalAltResult>> {
|
||||
listing.add_bid(bid).map_err(|e| e.into())
|
||||
}
|
||||
);
|
||||
engine.register_fn("get_bids_cloned", |listing: &mut Listing| listing.bids.clone());
|
||||
engine.register_fn("complete_listing_sale_script",
|
||||
|listing: Listing, buyer_id_rhai: ImmutableString, sale_price_rhai: f64| -> Result<Listing, Box<EvalAltResult>> {
|
||||
listing.complete_sale(buyer_id_rhai.as_str(), sale_price_rhai).map_err(|e| e.into())
|
||||
}
|
||||
);
|
||||
engine.register_fn("cancel_listing_script",
|
||||
|listing: Listing| -> Result<Listing, Box<EvalAltResult>> {
|
||||
listing.cancel().map_err(|e| e.into())
|
||||
}
|
||||
);
|
||||
engine.register_fn("add_listing_tags_script",
|
||||
|listing: Listing, tags_dyn_rhai: Array| -> Result<Listing, Box<EvalAltResult>> {
|
||||
let tags_to_add: Vec<String> = tags_dyn_rhai.into_iter().map(|d| d.into_string().unwrap_or_default()).collect();
|
||||
Ok(listing.add_tags(tags_to_add))
|
||||
}
|
||||
);
|
||||
|
||||
// --- Global Helper Functions (Enum conversions, potentially already covered by macros but good for direct script use) ---
|
||||
// These are useful if scripts need to convert strings to enums outside of object setters.
|
||||
engine.register_fn("str_to_asset_type", |s: ImmutableString| self::string_to_asset_type(s.as_str()));
|
||||
engine.register_fn("asset_type_to_str", self::asset_type_to_string);
|
||||
engine.register_fn("str_to_listing_status", |s: ImmutableString| self::string_to_listing_status(s.as_str()));
|
||||
engine.register_fn("listing_status_to_str", self::listing_status_to_string);
|
||||
engine.register_fn("str_to_listing_type", |s: ImmutableString| self::string_to_listing_type(s.as_str()));
|
||||
engine.register_fn("listing_type_to_str", self::listing_type_to_string);
|
||||
engine.register_fn("str_to_bid_status", |s: ImmutableString| self::string_to_bid_status(s.as_str()));
|
||||
engine.register_fn("bid_status_to_str", self::bid_status_to_string);
|
||||
|
||||
|
||||
// --- Mock DB functions ---
|
||||
let accounts_db_clone = Arc::clone(&db_accounts);
|
||||
engine.register_fn("set_account", move |account: Account| {
|
||||
let mut db = accounts_db_clone.lock().unwrap();
|
||||
db.insert(account.base_data.id, account);
|
||||
});
|
||||
|
||||
let accounts_db_clone_get = Arc::clone(&db_accounts);
|
||||
engine.register_fn("get_account_by_id", move |id_rhai: INT| -> Result<Account, Box<EvalAltResult>> {
|
||||
let db = accounts_db_clone_get.lock().unwrap();
|
||||
match db.get(&(id_rhai as u32)) {
|
||||
Some(account) => Ok(account.clone()),
|
||||
None => Err(format!("Account not found with ID: {}", id_rhai).into()),
|
||||
}
|
||||
});
|
||||
|
||||
let assets_db_clone = Arc::clone(&db_assets);
|
||||
engine.register_fn("set_asset", move |asset: Asset| {
|
||||
let mut db = assets_db_clone.lock().unwrap();
|
||||
db.insert(asset.base_data.id, asset);
|
||||
});
|
||||
|
||||
let assets_db_clone_get = Arc::clone(&db_assets);
|
||||
engine.register_fn("get_asset_by_id", move |id_rhai: INT| -> Result<Asset, Box<EvalAltResult>> {
|
||||
let db = assets_db_clone_get.lock().unwrap();
|
||||
match db.get(&(id_rhai as u32)) {
|
||||
Some(asset) => Ok(asset.clone()),
|
||||
None => Err(format!("Asset not found with ID: {}", id_rhai).into()),
|
||||
}
|
||||
});
|
||||
|
||||
let listings_db_clone = Arc::clone(&db_listings);
|
||||
engine.register_fn("set_listing", move |listing: Listing| {
|
||||
let mut db = listings_db_clone.lock().unwrap();
|
||||
db.insert(listing.base_data.id, listing);
|
||||
});
|
||||
|
||||
let listings_db_clone_get = Arc::clone(&db_listings);
|
||||
engine.register_fn("get_listing_by_id", move |id_rhai: INT| -> Result<Listing, Box<EvalAltResult>> {
|
||||
let db = listings_db_clone_get.lock().unwrap();
|
||||
match db.get(&(id_rhai as u32)) {
|
||||
Some(listing) => Ok(listing.clone()),
|
||||
None => Err(format!("Listing not found with ID: {}", id_rhai).into()),
|
||||
}
|
||||
});
|
||||
|
||||
// Global timestamp function for scripts to get current time
|
||||
engine.register_fn("timestamp", || Utc::now().timestamp());
|
||||
}
|
@@ -1,9 +1,10 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::flow_step::FlowStep;
|
||||
|
||||
/// Represents a signing flow.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[model]
|
||||
pub struct Flow {
|
||||
/// Base model data (id, created_at, updated_at).
|
||||
@@ -19,29 +20,37 @@ pub struct Flow {
|
||||
|
||||
/// 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, name: impl ToString, status: impl ToString) -> Self {
|
||||
pub fn new(id: u32, flow_uuid: impl ToString) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
flow_uuid: flow_uuid.to_string(),
|
||||
name: name.to_string(),
|
||||
status: status.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(),
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods for optional fields or to change initial values can be added here if needed.
|
||||
// For example:
|
||||
// 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 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
|
||||
}
|
||||
}
|
@@ -3,16 +3,12 @@ use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents a step within a signing flow.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[model]
|
||||
pub struct FlowStep {
|
||||
/// Base model data.
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// Foreign key to the Flow this step belongs to.
|
||||
#[index]
|
||||
pub flow_id: u32,
|
||||
|
||||
/// Optional description for the step.
|
||||
pub description: Option<String>,
|
||||
|
||||
@@ -26,13 +22,12 @@ pub struct FlowStep {
|
||||
|
||||
impl FlowStep {
|
||||
/// Create a new flow step.
|
||||
pub fn new(id: u32, flow_id: u32, step_order: u32, status: impl ToString) -> Self {
|
||||
pub fn new(id: u32, step_order: u32) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
flow_id,
|
||||
description: None,
|
||||
step_order,
|
||||
status: status.to_string(),
|
||||
status: String::from("Pending"), // Default status
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +37,8 @@ impl FlowStep {
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn status(mut self, status: impl ToString) -> Self {
|
||||
// self.status = status.to_string();
|
||||
// self
|
||||
// }
|
||||
pub fn status(mut self, status: impl ToString) -> Self {
|
||||
self.status = status.to_string();
|
||||
self
|
||||
}
|
||||
}
|
@@ -1,9 +1,11 @@
|
||||
// Export flowbroker model submodules
|
||||
// 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;
|
140
heromodels/src/models/flow/rhai.rs
Normal file
140
heromodels/src/models/flow/rhai.rs
Normal 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.");
|
||||
}
|
@@ -31,7 +31,7 @@ pub struct SignatureRequirement {
|
||||
|
||||
impl SignatureRequirement {
|
||||
/// Create a new signature requirement.
|
||||
pub fn new(id: u32, flow_step_id: u32, public_key: impl ToString, message: impl ToString, status: impl ToString) -> Self {
|
||||
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,
|
||||
@@ -39,7 +39,7 @@ impl SignatureRequirement {
|
||||
message: message.to_string(),
|
||||
signed_by: None,
|
||||
signature: None,
|
||||
status: status.to_string(),
|
||||
status: String::from("Pending"), // Default status
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ impl SignatureRequirement {
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn status(mut self, status: impl ToString) -> Self {
|
||||
// self.status = status.to_string();
|
||||
// self
|
||||
// }
|
||||
pub fn status(mut self, status: impl ToString) -> Self {
|
||||
self.status = status.to_string();
|
||||
self
|
||||
}
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
pub mod contract;
|
||||
pub mod rhai;
|
||||
|
||||
pub use contract::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
|
||||
pub use rhai::register_legal_rhai_module;
|
||||
|
265
heromodels/src/models/legal/rhai.rs
Normal file
265
heromodels/src/models/legal/rhai.rs
Normal file
@@ -0,0 +1,265 @@
|
||||
use rhai::{
|
||||
Dynamic, Engine, EvalAltResult, NativeCallContext, Position, Module, Array,
|
||||
};
|
||||
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_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,
|
||||
)))
|
||||
});
|
||||
}
|
@@ -5,6 +5,10 @@ pub mod userexample;
|
||||
pub mod calendar;
|
||||
pub mod governance;
|
||||
pub mod finance;
|
||||
pub mod legal;
|
||||
pub mod flow;
|
||||
pub mod biz;
|
||||
pub mod projects;
|
||||
|
||||
// Re-export key types for convenience
|
||||
pub use core::Comment;
|
||||
@@ -14,3 +18,14 @@ pub use calendar::{Calendar, Event, Attendee, AttendanceStatus};
|
||||
pub use governance::{Proposal, ProposalStatus, VoteEventStatus, Ballot, VoteOption};
|
||||
pub use finance::{Account, Asset, AssetType};
|
||||
pub use finance::marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus};
|
||||
pub use legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
|
||||
pub use flow::{Flow, FlowStep, SignatureRequirement};
|
||||
pub use biz::{Sale, SaleItem, SaleStatus};
|
||||
|
||||
pub use flow::register_flow_rhai_module;
|
||||
pub use calendar::register_calendar_rhai_module;
|
||||
pub use legal::register_legal_rhai_module;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use biz::register_biz_rhai_module;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use projects::register_projects_rhai_module;
|
||||
|
341
heromodels/src/models/projects/base.rs
Normal file
341
heromodels/src/models/projects/base.rs
Normal file
@@ -0,0 +1,341 @@
|
||||
// heromodels/src/models/projects/base.rs
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::{BaseModelData, Model};
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
use rhai::{CustomType, TypeBuilder}; // Removed Engine, EvalAltResult, Dynamic, Position, ImmutableString
|
||||
|
||||
// --- Enums ---
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
// #[cfg_attr(feature = "rhai", derive(CustomType))] // Removed derive for enum
|
||||
pub enum Priority {
|
||||
Critical,
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
None,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
impl CustomType for Priority {
|
||||
fn build(mut builder: TypeBuilder<Self>) { // Takes mut builder, returns ()
|
||||
println!("DEBUG: CustomType::build for Priority called!");
|
||||
builder
|
||||
.with_name("Priority")
|
||||
.with_fn("to_string", |p_mut: &mut Priority| {
|
||||
let rust_string = ToString::to_string(p_mut);
|
||||
println!("DEBUG: Priority.to_string() in Rust (via Rhai) produced: '{}'", rust_string);
|
||||
rust_string
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Priority {
|
||||
fn default() -> Self {
|
||||
Priority::None
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Priority {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Priority::Critical => "Critical".to_string(),
|
||||
Priority::High => "High".to_string(),
|
||||
Priority::Medium => "Medium".to_string(),
|
||||
Priority::Low => "Low".to_string(),
|
||||
Priority::None => "None".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
// #[cfg_attr(feature = "rhai", derive(CustomType))] // Removed derive for enum
|
||||
pub enum Status {
|
||||
Todo,
|
||||
InProgress,
|
||||
Review,
|
||||
Done,
|
||||
Archived,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
impl CustomType for Status {
|
||||
fn build(mut builder: TypeBuilder<Self>) { // Takes mut builder, returns ()
|
||||
println!("DEBUG: CustomType::build for Status called!");
|
||||
builder
|
||||
.with_name("Status")
|
||||
.with_fn("to_string", |s_mut: &mut Status| {
|
||||
let rust_string = ToString::to_string(s_mut);
|
||||
println!("DEBUG: Status.to_string() in Rust (via Rhai) produced: '{}'", rust_string);
|
||||
rust_string
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Status {
|
||||
fn default() -> Self {
|
||||
Status::Todo
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Status {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Status::Todo => "Todo".to_string(),
|
||||
Status::InProgress => "InProgress".to_string(),
|
||||
Status::Review => "Review".to_string(),
|
||||
Status::Done => "Done".to_string(),
|
||||
Status::Archived => "Archived".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
// #[cfg_attr(feature = "rhai", derive(CustomType))] // Removed derive for enum
|
||||
pub enum ItemType {
|
||||
Epic,
|
||||
Story,
|
||||
Task,
|
||||
Bug,
|
||||
Improvement,
|
||||
Feature,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
impl CustomType for ItemType {
|
||||
fn build(mut builder: TypeBuilder<Self>) { // Takes mut builder, returns ()
|
||||
println!("DEBUG: CustomType::build for ItemType called!");
|
||||
builder
|
||||
.with_name("ItemType")
|
||||
.with_fn("to_string", |it_mut: &mut ItemType| {
|
||||
let rust_string = ToString::to_string(it_mut);
|
||||
println!("DEBUG: ItemType.to_string() in Rust (via Rhai) produced: '{}'", rust_string);
|
||||
rust_string
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ItemType {
|
||||
fn default() -> Self {
|
||||
ItemType::Task
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for ItemType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
ItemType::Epic => "Epic".to_string(),
|
||||
ItemType::Story => "Story".to_string(),
|
||||
ItemType::Task => "Task".to_string(),
|
||||
ItemType::Bug => "Bug".to_string(),
|
||||
ItemType::Improvement => "Improvement".to_string(),
|
||||
ItemType::Feature => "Feature".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Structs ---
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "rhai", derive(CustomType))]
|
||||
pub struct Project {
|
||||
pub base_data: BaseModelData,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub owner_id: u32,
|
||||
pub member_ids: Vec<u32>,
|
||||
pub board_ids: Vec<u32>,
|
||||
pub sprint_ids: Vec<u32>,
|
||||
pub epic_ids: Vec<u32>,
|
||||
pub tags: Vec<String>,
|
||||
pub status: Status,
|
||||
pub priority: Priority,
|
||||
pub item_type: ItemType,
|
||||
}
|
||||
|
||||
impl Model for Project {
|
||||
fn get_id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
fn db_prefix() -> &'static str {
|
||||
"prj"
|
||||
}
|
||||
fn base_data_mut(&mut self) -> &mut BaseModelData {
|
||||
&mut self.base_data
|
||||
}
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn new(id: u32, name: String, description: String, owner_id: u32) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
name,
|
||||
description,
|
||||
owner_id,
|
||||
member_ids: Vec::new(),
|
||||
board_ids: Vec::new(),
|
||||
sprint_ids: Vec::new(),
|
||||
epic_ids: Vec::new(),
|
||||
tags: Vec::new(),
|
||||
status: Status::default(),
|
||||
priority: Priority::default(),
|
||||
item_type: ItemType::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
pub fn description(mut self, description: String) -> Self {
|
||||
self.description = description;
|
||||
self
|
||||
}
|
||||
pub fn owner_id(mut self, owner_id: u32) -> Self {
|
||||
self.owner_id = owner_id;
|
||||
self
|
||||
}
|
||||
pub fn add_member_id(mut self, member_id: u32) -> Self {
|
||||
self.member_ids.push(member_id);
|
||||
self
|
||||
}
|
||||
pub fn member_ids(mut self, member_ids: Vec<u32>) -> Self {
|
||||
self.member_ids = member_ids;
|
||||
self
|
||||
}
|
||||
pub fn add_board_id(mut self, board_id: u32) -> Self {
|
||||
self.board_ids.push(board_id);
|
||||
self
|
||||
}
|
||||
pub fn board_ids(mut self, board_ids: Vec<u32>) -> Self {
|
||||
self.board_ids = board_ids;
|
||||
self
|
||||
}
|
||||
pub fn add_sprint_id(mut self, sprint_id: u32) -> Self {
|
||||
self.sprint_ids.push(sprint_id);
|
||||
self
|
||||
}
|
||||
pub fn sprint_ids(mut self, sprint_ids: Vec<u32>) -> Self {
|
||||
self.sprint_ids = sprint_ids;
|
||||
self
|
||||
}
|
||||
pub fn add_epic_id(mut self, epic_id: u32) -> Self {
|
||||
self.epic_ids.push(epic_id);
|
||||
self
|
||||
}
|
||||
pub fn epic_ids(mut self, epic_ids: Vec<u32>) -> Self {
|
||||
self.epic_ids = epic_ids;
|
||||
self
|
||||
}
|
||||
pub fn add_tag(mut self, tag: String) -> Self {
|
||||
self.tags.push(tag);
|
||||
self
|
||||
}
|
||||
pub fn tags(mut self, tags: Vec<String>) -> Self {
|
||||
self.tags = tags;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn status(mut self, status: Status) -> Self {
|
||||
self.status = status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn priority(mut self, priority: Priority) -> Self {
|
||||
self.priority = priority;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn item_type(mut self, item_type: ItemType) -> Self {
|
||||
self.item_type = item_type;
|
||||
self
|
||||
}
|
||||
|
||||
// Base model builder methods
|
||||
pub fn set_base_id(mut self, id: u32) -> Self {
|
||||
self.base_data.id = id;
|
||||
self
|
||||
}
|
||||
pub fn set_base_created_at(mut self, time: i64) -> Self {
|
||||
self.base_data.created_at = time;
|
||||
self
|
||||
}
|
||||
pub fn set_base_modified_at(mut self, time: i64) -> Self {
|
||||
self.base_data.modified_at = time;
|
||||
self
|
||||
}
|
||||
pub fn add_base_comment(mut self, comment_id: u32) -> Self {
|
||||
self.base_data.comments.push(comment_id);
|
||||
self
|
||||
}
|
||||
pub fn set_base_comments(mut self, comment_ids: Vec<u32>) -> Self {
|
||||
self.base_data.comments = comment_ids;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "rhai", derive(CustomType))]
|
||||
pub struct Label {
|
||||
pub base_data: BaseModelData,
|
||||
pub name: String,
|
||||
pub color: String, // Hex color code
|
||||
}
|
||||
|
||||
impl Model for Label {
|
||||
fn get_id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
fn db_prefix() -> &'static str {
|
||||
"lbl"
|
||||
}
|
||||
fn base_data_mut(&mut self) -> &mut BaseModelData {
|
||||
&mut self.base_data
|
||||
}
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new(id: u32, name: String, color: String) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(id),
|
||||
name,
|
||||
color,
|
||||
}
|
||||
}
|
||||
|
||||
// Builder methods
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
pub fn color(mut self, color: String) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
// Base model builder methods
|
||||
pub fn set_base_id(mut self, id: u32) -> Self {
|
||||
self.base_data.id = id;
|
||||
self
|
||||
}
|
||||
pub fn set_base_created_at(mut self, time: i64) -> Self {
|
||||
self.base_data.created_at = time;
|
||||
self
|
||||
}
|
||||
pub fn set_base_modified_at(mut self, time: i64) -> Self {
|
||||
self.base_data.modified_at = time;
|
||||
self
|
||||
}
|
||||
pub fn add_base_comment(mut self, comment_id: u32) -> Self {
|
||||
self.base_data.comments.push(comment_id);
|
||||
self
|
||||
}
|
||||
pub fn set_base_comments(mut self, comment_ids: Vec<u32>) -> Self {
|
||||
self.base_data.comments = comment_ids;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
21
heromodels/src/models/projects/mod.rs
Normal file
21
heromodels/src/models/projects/mod.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
// heromodels/src/models/projects/mod.rs
|
||||
|
||||
pub mod base;
|
||||
// pub mod epic;
|
||||
// pub mod issue;
|
||||
// pub mod kanban;
|
||||
// pub mod sprint;
|
||||
// pub mod story;
|
||||
|
||||
pub use base::*;
|
||||
// pub use epic::*;
|
||||
// pub use issue::*;
|
||||
// pub use kanban::*;
|
||||
// pub use sprint::*;
|
||||
// pub use story::*;
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub mod rhai;
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use rhai::register_projects_rhai_module;
|
271
heromodels/src/models/projects/rhai.rs
Normal file
271
heromodels/src/models/projects/rhai.rs
Normal file
@@ -0,0 +1,271 @@
|
||||
// heromodels/src/models/projects/rhai.rs
|
||||
|
||||
use rhai::{Engine, EvalAltResult, Position, Dynamic};
|
||||
use crate::db::Db; // Added to bring Db trait and .collection() method into scope
|
||||
use std::sync::Arc;
|
||||
use crate::db::hero::OurDB; // Corrected path
|
||||
use heromodels_core::Model; // Added
|
||||
use crate::db::Collection;
|
||||
|
||||
|
||||
// Import models from the projects::base module
|
||||
use super::base::{Project, /* Label, */ Priority, Status, ItemType}; // Label commented out as it's unused for now
|
||||
|
||||
// Helper function for ID conversion (if needed, similar to other rhai.rs files)
|
||||
fn id_from_i64(val: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
if val < 0 {
|
||||
Err(EvalAltResult::ErrorArithmetic(
|
||||
format!("ID value cannot be negative: {}", val),
|
||||
rhai::Position::NONE,
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Ok(val as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register enums as constants (example for Priority)
|
||||
engine.register_static_module("Priority", {
|
||||
let mut module = rhai::Module::new();
|
||||
module.set_var("Critical", Priority::Critical);
|
||||
module.set_var("High", Priority::High);
|
||||
module.set_var("Medium", Priority::Medium);
|
||||
module.set_var("Low", Priority::Low);
|
||||
module.set_var("None", Priority::None);
|
||||
module.into()
|
||||
});
|
||||
|
||||
engine.register_static_module("Status", {
|
||||
let mut module = rhai::Module::new();
|
||||
module.set_var("Todo", Status::Todo);
|
||||
module.set_var("InProgress", Status::InProgress);
|
||||
module.set_var("Review", Status::Review);
|
||||
module.set_var("Done", Status::Done);
|
||||
module.set_var("Archived", Status::Archived);
|
||||
module.into()
|
||||
});
|
||||
|
||||
engine.register_static_module("ItemType", {
|
||||
let mut module = rhai::Module::new();
|
||||
module.set_var("Epic", ItemType::Epic);
|
||||
module.set_var("Story", ItemType::Story);
|
||||
module.set_var("Task", ItemType::Task);
|
||||
module.set_var("Bug", ItemType::Bug);
|
||||
module.set_var("Improvement", ItemType::Improvement);
|
||||
module.set_var("Feature", ItemType::Feature);
|
||||
module.into()
|
||||
});
|
||||
|
||||
// --- Enum Type Registration ---
|
||||
engine.register_type_with_name::<Priority>("Priority");
|
||||
engine.register_type_with_name::<Status>("Status");
|
||||
engine.register_type_with_name::<ItemType>("ItemType");
|
||||
|
||||
// --- Project Registration ---
|
||||
engine.register_type_with_name::<Project>("Project");
|
||||
|
||||
// Constructor for Project
|
||||
// Zero-argument constructor
|
||||
engine.register_fn("new_project", || -> Result<Project, Box<EvalAltResult>> {
|
||||
// Assuming Project::new() or Project::default() can be used.
|
||||
// If Project::new() requires args, this needs adjustment or Project needs Default impl.
|
||||
Ok(Project::new(0, "".to_string(), "".to_string(), 0))
|
||||
});
|
||||
|
||||
// Multi-argument constructor (renamed)
|
||||
engine.register_fn("new_project_with_details", |id_i64: i64, name: String, description: String, owner_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(Project::new(id_from_i64(id_i64)?, name, description, id_from_i64(owner_id_i64)?))
|
||||
});
|
||||
|
||||
// Getters for Project
|
||||
engine.register_get("id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.get_id() as i64) });
|
||||
engine.register_get("name", |p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.name.clone()) });
|
||||
engine.register_get("description", |p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.description.clone()) });
|
||||
engine.register_get("owner_id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.owner_id as i64) });
|
||||
engine.register_get("member_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.member_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("board_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.board_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("sprint_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.sprint_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("epic_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.epic_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("tags", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.tags.iter().map(|tag| rhai::Dynamic::from(tag.clone())).collect())
|
||||
});
|
||||
engine.register_get("created_at", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.created_at) });
|
||||
engine.register_get("modified_at", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.modified_at) });
|
||||
engine.register_get("comments", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.base_data.comments.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("status", |p: &mut Project| -> Result<Status, Box<EvalAltResult>> { Ok(p.status.clone()) });
|
||||
engine.register_get("priority", |p: &mut Project| -> Result<Priority, Box<EvalAltResult>> { Ok(p.priority.clone()) });
|
||||
engine.register_get("item_type", |p: &mut Project| -> Result<ItemType, Box<EvalAltResult>> { Ok(p.item_type.clone()) });
|
||||
|
||||
// Builder methods for Project
|
||||
// let db_clone = db.clone(); // This was unused
|
||||
engine.register_fn("name", |p: Project, name: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.name(name)) });
|
||||
engine.register_fn("description", |p: Project, description: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.description(description)) });
|
||||
engine.register_fn("owner_id", |p: Project, owner_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.owner_id(id_from_i64(owner_id_i64)?)) });
|
||||
engine.register_fn("add_member_id", |p: Project, member_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_member_id(id_from_i64(member_id_i64)?)) });
|
||||
engine.register_fn("member_ids", |p: Project, member_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = member_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.member_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_board_id", |p: Project, board_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_board_id(id_from_i64(board_id_i64)?)) });
|
||||
engine.register_fn("board_ids", |p: Project, board_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = board_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.board_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_sprint_id", |p: Project, sprint_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_sprint_id(id_from_i64(sprint_id_i64)?)) });
|
||||
engine.register_fn("sprint_ids", |p: Project, sprint_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = sprint_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.sprint_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_epic_id", |p: Project, epic_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_epic_id(id_from_i64(epic_id_i64)?)) });
|
||||
engine.register_fn("epic_ids", |p: Project, epic_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = epic_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.epic_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_tag", |p: Project, tag: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_tag(tag)) });
|
||||
engine.register_fn("tags", |p: Project, tags_dyn: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let tags_vec = tags_dyn
|
||||
.into_iter()
|
||||
.map(|tag_dyn: Dynamic| {
|
||||
tag_dyn.clone().into_string().map_err(|_err| { // _err is Rhai's internal error, we create a new one
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected string for tag".to_string(),
|
||||
tag_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<String>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.tags(tags_vec))
|
||||
});
|
||||
|
||||
engine.register_fn("status", |p: Project, status: Status| -> Result<Project, Box<EvalAltResult>> { Ok(p.status(status)) });
|
||||
engine.register_fn("priority", |p: Project, priority: Priority| -> Result<Project, Box<EvalAltResult>> { Ok(p.priority(priority)) });
|
||||
engine.register_fn("item_type", |p: Project, item_type: ItemType| -> Result<Project, Box<EvalAltResult>> { Ok(p.item_type(item_type)) });
|
||||
// Base ModelData builders
|
||||
engine.register_fn("set_base_id", |p: Project, id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.set_base_id(id_from_i64(id_i64)?)) });
|
||||
engine.register_fn("set_base_created_at", |p: Project, time: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.set_base_created_at(time)) });
|
||||
engine.register_fn("set_base_modified_at", |p: Project, time: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.set_base_modified_at(time)) });
|
||||
engine.register_fn("add_base_comment", |p: Project, comment_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_base_comment(id_from_i64(comment_id_i64)?)) });
|
||||
engine.register_fn("set_base_comments", |p: Project, comment_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = comment_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.set_base_comments(ids))
|
||||
});
|
||||
|
||||
// --- Database Interaction Functions ---
|
||||
let db_clone_set = db.clone();
|
||||
engine.register_fn("set_project", move |project: Project| -> Result<(), Box<EvalAltResult>> {
|
||||
let collection = db_clone_set.collection::<Project>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to access project collection".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))
|
||||
})?;
|
||||
|
||||
collection.set(&project).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to save project".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))
|
||||
})
|
||||
});
|
||||
|
||||
let db_clone_get = db.clone();
|
||||
engine.register_fn("get_project_by_id", move |id_i64: i64| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id = id_from_i64(id_i64)?;
|
||||
let collection = db_clone_get.collection::<Project>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to access project collection".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))
|
||||
})?;
|
||||
|
||||
match collection.get_by_id(id) {
|
||||
Ok(Some(project)) => Ok(Dynamic::from(project)),
|
||||
Ok(None) => Ok(Dynamic::UNIT), // Represents '()' in Rhai
|
||||
Err(e) => Err(Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to retrieve project by ID".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))),
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Register Rhai bindings for the `Label` model if needed, or remove unused import.
|
||||
// Register Label type and its methods/getters
|
||||
// engine.register_type_with_name::<Label>("Label")
|
||||
// .register_fn("new_label", Label::new) // Simplified
|
||||
// // ... other Label methods and getters ...
|
||||
// ;
|
||||
|
||||
// TODO: Add DB interaction functions like set_project, get_project_by_id etc.
|
||||
}
|
Reference in New Issue
Block a user