fmt, fixes and additions

This commit is contained in:
timurgordon
2025-06-19 13:18:10 +03:00
parent 6b3cbfc4b2
commit e91a44ce37
86 changed files with 5292 additions and 2844 deletions

View File

@@ -57,7 +57,9 @@ where
fn get_all(&self) -> Result<Vec<V>, Error<Self::Error>>;
/// Begin a transaction for this collection
fn begin_transaction(&self) -> Result<Box<dyn Transaction<Error = Self::Error>>, Error<Self::Error>>;
fn begin_transaction(
&self,
) -> Result<Box<dyn Transaction<Error = Self::Error>>, Error<Self::Error>>;
}
/// Errors returned by the DB implementation

View File

@@ -436,8 +436,12 @@ where
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)
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) => {

View File

@@ -55,4 +55,4 @@ impl Access {
self.expires_at = expires_at;
self
}
}
}

View File

@@ -3,5 +3,5 @@ pub mod access;
pub mod rhai;
// Re-export contact, Group from the inner contact module (contact.rs) within src/models/contact/mod.rs
pub use self::access::{Access};
pub use self::access::Access;
pub use rhai::register_access_rhai_module;

View File

@@ -1,22 +1,22 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use super::access::{Access};
use super::access::Access;
type RhaiAccess = Access;
use crate::db::hero::OurDB;
use crate::db::Collection;
use crate::db::hero::OurDB;
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
u32::try_from(id_i64).map_err(|_|
u32::try_from(id_i64).map_err(|_| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert ID '{}' to u32", id_i64).into(),
Position::NONE
Position::NONE,
))
)
})
}
#[export_module]
@@ -27,13 +27,16 @@ mod rhai_access_module {
let access = Access::new();
Ok(access)
}
/// Sets the access name
#[rhai_fn(name = "object_id", return_raw, global, pure)]
pub fn access_object_id(access: &mut RhaiAccess, object_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
pub fn access_object_id(
access: &mut RhaiAccess,
object_id: u32,
) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.object_id(object_id);
@@ -41,21 +44,27 @@ mod rhai_access_module {
}
#[rhai_fn(name = "circle_id", return_raw, global, pure)]
pub fn access_circle_id(access: &mut RhaiAccess, circle_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
pub fn access_circle_id(
access: &mut RhaiAccess,
circle_id: u32,
) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.circle_id(circle_id);
Ok(access.clone())
}
#[rhai_fn(name = "group_id", return_raw, global, pure)]
pub fn access_group_id(access: &mut RhaiAccess, group_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
pub fn access_group_id(
access: &mut RhaiAccess,
group_id: u32,
) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.group_id(group_id);
@@ -63,10 +72,13 @@ mod rhai_access_module {
}
#[rhai_fn(name = "contact_id", return_raw, global, pure)]
pub fn access_contact_id(access: &mut RhaiAccess, contact_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
pub fn access_contact_id(
access: &mut RhaiAccess,
contact_id: u32,
) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.contact_id(contact_id);
@@ -74,10 +86,13 @@ mod rhai_access_module {
}
#[rhai_fn(name = "expires_at", return_raw, global, pure)]
pub fn access_expires_at(access: &mut RhaiAccess, expires_at: Option<u64>) -> Result<RhaiAccess, Box<EvalAltResult>> {
pub fn access_expires_at(
access: &mut RhaiAccess,
expires_at: Option<u64>,
) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.expires_at(expires_at);
@@ -86,90 +101,136 @@ mod rhai_access_module {
// Access Getters
#[rhai_fn(get = "id", pure)]
pub fn get_access_id(access: &mut RhaiAccess) -> i64 { access.base_data.id as i64 }
pub fn get_access_id(access: &mut RhaiAccess) -> i64 {
access.base_data.id as i64
}
#[rhai_fn(get = "object_id", pure)]
pub fn get_access_object_id(access: &mut RhaiAccess) -> i64 { access.object_id as i64 }
pub fn get_access_object_id(access: &mut RhaiAccess) -> i64 {
access.object_id as i64
}
#[rhai_fn(get = "circle_id", pure)]
pub fn get_access_circle_id(access: &mut RhaiAccess) -> i64 { access.circle_id as i64 }
pub fn get_access_circle_id(access: &mut RhaiAccess) -> i64 {
access.circle_id as i64
}
#[rhai_fn(get = "group_id", pure)]
pub fn get_access_group_id(access: &mut RhaiAccess) -> i64 { access.group_id as i64 }
pub fn get_access_group_id(access: &mut RhaiAccess) -> i64 {
access.group_id as i64
}
#[rhai_fn(get = "contact_id", pure)]
pub fn get_access_contact_id(access: &mut RhaiAccess) -> i64 { access.contact_id as i64 }
pub fn get_access_contact_id(access: &mut RhaiAccess) -> i64 {
access.contact_id as i64
}
#[rhai_fn(get = "expires_at", pure)]
pub fn get_access_expires_at(access: &mut RhaiAccess) -> i64 { access.expires_at.unwrap_or(0) as i64 }
pub fn get_access_expires_at(access: &mut RhaiAccess) -> i64 {
access.expires_at.unwrap_or(0) as i64
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_access_created_at(access: &mut RhaiAccess) -> i64 { access.base_data.created_at }
pub fn get_access_created_at(access: &mut RhaiAccess) -> i64 {
access.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_access_modified_at(access: &mut RhaiAccess) -> i64 { access.base_data.modified_at }
pub fn get_access_modified_at(access: &mut RhaiAccess) -> i64 {
access.base_data.modified_at
}
}
pub fn register_access_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_access_module);
engine.register_global_module(module.into());
// Create a module for database functions
let mut db_module = Module::new();
let db_clone_set_access = db.clone();
db_module.set_native_fn("save_access", move |access: Access| -> Result<Access, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_access.set(&access)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_access: {}", e).into(), Position::NONE)))?;
// Return the updated access with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_access",
move |access: Access| -> Result<Access, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_access.set(&access).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error set_access: {}", e).into(),
Position::NONE,
))
})?;
// Return the updated access with the correct ID
Ok(result.1)
},
);
// Manually register database functions as they need to capture 'db'
let db_clone_delete_access = db.clone();
db_module.set_native_fn("delete_access", move |access: Access| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_access.collection::<Access>()
.expect("can open access collection")
.delete_by_id(access.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
});
db_module.set_native_fn(
"delete_access",
move |access: Access| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_access
.collection::<Access>()
.expect("can open access collection")
.delete_by_id(access.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
},
);
let db_clone_get_access = db.clone();
db_module.set_native_fn("get_access_by_id", move |id_i64: INT| -> Result<Access, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_access.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_access_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Access with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_access_by_id",
move |id_i64: INT| -> Result<Access, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_access
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_access_by_id: {}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Access with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
// Add list_accesss function to get all accesss
let db_clone_list_accesss = db.clone();
db_module.set_native_fn("list_accesss", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_accesss.collection::<Access>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get access collection: {:?}", e).into(),
Position::NONE
)))?;
let accesss = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all accesss: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for access in accesss {
array.push(Dynamic::from(access));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_accesss",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_accesss.collection::<Access>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get access collection: {:?}", e).into(),
Position::NONE,
))
})?;
let accesss = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all accesss: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for access in accesss {
array.push(Dynamic::from(access));
}
Ok(Dynamic::from(array))
},
);
// Register the database module globally
engine.register_global_module(db_module.into());

View File

@@ -1,8 +1,8 @@
use serde::{Deserialize, Serialize};
use heromodels_core::{BaseModelData, Index};
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
use heromodels_core::BaseModelDataOps;
use heromodels_core::{BaseModelData, Index};
use heromodels_derive::model;
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
use serde::{Deserialize, Serialize};
// --- Enums ---
@@ -100,17 +100,17 @@ impl Company {
status: CompanyStatus::default(),
}
}
pub fn name(mut self, name: impl ToString) -> Self {
self.name = name.to_string();
self
}
pub fn registration_number(mut self, registration_number: impl ToString) -> Self {
self.registration_number = registration_number.to_string();
self
}
pub fn incorporation_date(mut self, incorporation_date: i64) -> Self {
self.incorporation_date = incorporation_date;
self

View File

@@ -8,17 +8,16 @@ pub mod product;
// pub mod user;
// Re-export main types from sub-modules
pub use company::{Company, CompanyStatus, BusinessType};
pub use company::{BusinessType, Company, CompanyStatus};
pub mod shareholder;
pub use product::{Product, ProductComponent, ProductStatus, ProductType};
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")]

View File

@@ -1,6 +1,6 @@
use serde::{Serialize, Deserialize};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
// ProductType represents the type of a product
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
@@ -137,7 +137,7 @@ impl Product {
self.components.push(component);
self
}
pub fn components(mut self, components: Vec<ProductComponent>) -> Self {
self.components = components;
self

View File

@@ -1,15 +1,15 @@
use rhai::plugin::*;
use rhai::{Engine, Module, Dynamic, EvalAltResult, Position, INT};
use std::sync::Arc;
use std::mem;
use crate::db::Collection; // For db.set and db.get_by_id
use crate::db::hero::OurDB;
use crate::db::Db;
use crate::db::hero::OurDB;
use rhai::plugin::*;
use rhai::{Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use super::company::{Company, CompanyStatus, BusinessType};
use crate::models::biz::shareholder::{Shareholder, ShareholderType};
use crate::models::biz::product::{Product, ProductType, ProductStatus, ProductComponent};
use super::company::{BusinessType, Company, CompanyStatus};
use crate::models::biz::product::{Product, ProductComponent, ProductStatus, ProductType};
use crate::models::biz::sale::{Sale, SaleItem, SaleStatus};
use crate::models::biz::shareholder::{Shareholder, ShareholderType};
use heromodels_core::Model;
type RhaiCompany = Company;
@@ -21,12 +21,12 @@ type RhaiSaleItem = SaleItem;
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
u32::try_from(id_i64).map_err(|_|
u32::try_from(id_i64).map_err(|_| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert ID '{}' to u32", id_i64).into(),
Position::NONE
Position::NONE,
))
)
})
}
#[export_module]
@@ -36,443 +36,541 @@ mod rhai_biz_module {
pub fn new_company() -> RhaiCompany {
Company::new()
}
// Company builder methods
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn company_name(company: &mut RhaiCompany, name: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
pub fn company_name(
company: &mut RhaiCompany,
name: String,
) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = mem::take(company);
*company = owned_company.name(name);
Ok(company.clone())
}
#[rhai_fn(name = "fiscal_year_end", return_raw, global, pure)]
pub fn company_fiscal_year_end(company: &mut RhaiCompany, fiscal_year_end: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
pub fn company_fiscal_year_end(
company: &mut RhaiCompany,
fiscal_year_end: String,
) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = mem::take(company);
*company = owned_company.fiscal_year_end(fiscal_year_end);
Ok(company.clone())
}
#[rhai_fn(name = "registration_number", return_raw, global, pure)]
pub fn company_registration_number(company: &mut RhaiCompany, reg_num: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
pub fn company_registration_number(
company: &mut RhaiCompany,
reg_num: String,
) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = mem::take(company);
*company = owned_company.registration_number(reg_num);
Ok(company.clone())
}
#[rhai_fn(name = "incorporation_date", return_raw, global, pure)]
pub fn company_incorporation_date(company: &mut RhaiCompany, date: i64) -> Result<RhaiCompany, Box<EvalAltResult>> {
pub fn company_incorporation_date(
company: &mut RhaiCompany,
date: i64,
) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = mem::take(company);
*company = owned_company.incorporation_date(date);
Ok(company.clone())
}
#[rhai_fn(name = "status", return_raw, global, pure)]
pub fn company_status(company: &mut RhaiCompany, status: CompanyStatus) -> Result<RhaiCompany, Box<EvalAltResult>> {
pub fn company_status(
company: &mut RhaiCompany,
status: CompanyStatus,
) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = mem::take(company);
*company = owned_company.status(status);
Ok(company.clone())
}
#[rhai_fn(name = "business_type", return_raw, global, pure)]
pub fn company_business_type(company: &mut RhaiCompany, business_type: BusinessType) -> Result<RhaiCompany, Box<EvalAltResult>> {
pub fn company_business_type(
company: &mut RhaiCompany,
business_type: BusinessType,
) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = mem::take(company);
*company = owned_company.business_type(business_type);
Ok(company.clone())
}
// Company getters
#[rhai_fn(name = "get_company_id")]
pub fn get_company_id(company: &mut RhaiCompany) -> i64 {
company.get_id() as i64
}
#[rhai_fn(name = "get_company_name")]
pub fn get_company_name(company: &mut RhaiCompany) -> String {
company.name.clone()
}
#[rhai_fn(name = "get_company_created_at")]
pub fn get_company_created_at(company: &mut RhaiCompany) -> i64 {
company.base_data.created_at
}
#[rhai_fn(name = "get_company_modified_at")]
pub fn get_company_modified_at(company: &mut RhaiCompany) -> i64 {
company.base_data.modified_at
}
#[rhai_fn(name = "get_company_registration_number")]
pub fn get_company_registration_number(company: &mut RhaiCompany) -> String {
company.registration_number.clone()
}
#[rhai_fn(name = "get_company_fiscal_year_end")]
pub fn get_company_fiscal_year_end(company: &mut RhaiCompany) -> String {
company.fiscal_year_end.clone()
}
#[rhai_fn(name = "get_company_incorporation_date")]
pub fn get_company_incorporation_date(company: &mut RhaiCompany) -> i64 {
company.incorporation_date
}
#[rhai_fn(name = "get_company_status")]
pub fn get_company_status(company: &mut RhaiCompany) -> CompanyStatus {
company.status.clone()
}
#[rhai_fn(name = "get_company_business_type")]
pub fn get_company_business_type(company: &mut RhaiCompany) -> BusinessType {
company.business_type.clone()
}
// --- Shareholder Functions ---
#[rhai_fn(name = "new_shareholder")]
pub fn new_shareholder() -> RhaiShareholder {
Shareholder::new()
}
// Shareholder builder methods
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn shareholder_name(shareholder: &mut RhaiShareholder, name: String) -> Result<RhaiShareholder, Box<EvalAltResult>> {
pub fn shareholder_name(
shareholder: &mut RhaiShareholder,
name: String,
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned_shareholder = mem::take(shareholder);
*shareholder = owned_shareholder.name(name);
Ok(shareholder.clone())
}
#[rhai_fn(name = "company_id", return_raw, global, pure)]
pub fn shareholder_company_id(shareholder: &mut RhaiShareholder, company_id: i64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
pub fn shareholder_company_id(
shareholder: &mut RhaiShareholder,
company_id: i64,
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let company_id_u32 = id_from_i64_to_u32(company_id)?;
let owned_shareholder = mem::take(shareholder);
*shareholder = owned_shareholder.company_id(company_id_u32);
Ok(shareholder.clone())
}
#[rhai_fn(name = "share_count", return_raw, global, pure)]
pub fn shareholder_share_count(shareholder: &mut RhaiShareholder, share_count: f64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned_shareholder = mem::take(shareholder);
pub fn shareholder_share_count(
shareholder: &mut RhaiShareholder,
share_count: f64,
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
shareholder.shares = share_count;
Ok(shareholder.clone())
}
#[rhai_fn(name = "type_", return_raw, global, pure)]
pub fn shareholder_type(shareholder: &mut RhaiShareholder, type_: ShareholderType) -> Result<RhaiShareholder, Box<EvalAltResult>> {
pub fn shareholder_type(
shareholder: &mut RhaiShareholder,
type_: ShareholderType,
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned_shareholder = mem::take(shareholder);
*shareholder = owned_shareholder.type_(type_);
Ok(shareholder.clone())
}
// Shareholder getters
#[rhai_fn(name = "get_shareholder_id")]
pub fn get_shareholder_id(shareholder: &mut RhaiShareholder) -> i64 {
shareholder.get_id() as i64
}
#[rhai_fn(name = "get_shareholder_name")]
pub fn get_shareholder_name(shareholder: &mut RhaiShareholder) -> String {
shareholder.name.clone()
}
#[rhai_fn(name = "get_shareholder_company_id")]
pub fn get_shareholder_company_id(shareholder: &mut RhaiShareholder) -> i64 {
shareholder.company_id as i64
}
#[rhai_fn(name = "get_shareholder_share_count")]
pub fn get_shareholder_share_count(shareholder: &mut RhaiShareholder) -> i64 {
shareholder.shares as i64
}
#[rhai_fn(name = "get_shareholder_type")]
pub fn get_shareholder_type(shareholder: &mut RhaiShareholder) -> ShareholderType {
shareholder.type_.clone()
}
// --- ProductComponent Functions ---
#[rhai_fn(name = "new_product_component")]
pub fn new_product_component() -> RhaiProductComponent {
ProductComponent::new()
}
// ProductComponent builder methods
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn product_component_name(component: &mut RhaiProductComponent, name: String) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
pub fn product_component_name(
component: &mut RhaiProductComponent,
name: String,
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned_component = mem::take(component);
*component = owned_component.name(name);
Ok(component.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn product_component_description(component: &mut RhaiProductComponent, description: String) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
pub fn product_component_description(
component: &mut RhaiProductComponent,
description: String,
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned_component = mem::take(component);
*component = owned_component.description(description);
Ok(component.clone())
}
#[rhai_fn(name = "quantity", return_raw, global, pure)]
pub fn product_component_quantity(component: &mut RhaiProductComponent, quantity: i64) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
pub fn product_component_quantity(
component: &mut RhaiProductComponent,
quantity: i64,
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned_component = mem::take(component);
*component = owned_component.quantity(quantity as u32);
Ok(component.clone())
}
// ProductComponent getters
#[rhai_fn(name = "get_product_component_name")]
pub fn get_product_component_name(component: &mut RhaiProductComponent) -> String {
component.name.clone()
}
#[rhai_fn(name = "get_product_component_description")]
pub fn get_product_component_description(component: &mut RhaiProductComponent) -> String {
component.description.clone()
}
#[rhai_fn(name = "get_product_component_quantity")]
pub fn get_product_component_quantity(component: &mut RhaiProductComponent) -> i64 {
component.quantity as i64
}
// --- Product Functions ---
#[rhai_fn(name = "new_product")]
pub fn new_product() -> RhaiProduct {
Product::new()
}
// Product builder methods
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn product_name(product: &mut RhaiProduct, name: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_name(
product: &mut RhaiProduct,
name: String,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.name(name);
Ok(product.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn product_description(product: &mut RhaiProduct, description: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_description(
product: &mut RhaiProduct,
description: String,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.description(description);
Ok(product.clone())
}
#[rhai_fn(name = "price", return_raw, global, pure)]
pub fn product_price(product: &mut RhaiProduct, price: f64) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_price(
product: &mut RhaiProduct,
price: f64,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.price(price);
Ok(product.clone())
}
#[rhai_fn(name = "type_", return_raw, global, pure)]
pub fn product_type(product: &mut RhaiProduct, type_: ProductType) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_type(
product: &mut RhaiProduct,
type_: ProductType,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.type_(type_);
Ok(product.clone())
}
#[rhai_fn(name = "category", return_raw, global, pure)]
pub fn product_category(product: &mut RhaiProduct, category: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_category(
product: &mut RhaiProduct,
category: String,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.category(category);
Ok(product.clone())
}
#[rhai_fn(name = "status", return_raw, global, pure)]
pub fn product_status(product: &mut RhaiProduct, status: ProductStatus) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_status(
product: &mut RhaiProduct,
status: ProductStatus,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.status(status);
Ok(product.clone())
}
#[rhai_fn(name = "max_amount", return_raw, global, pure)]
pub fn product_max_amount(product: &mut RhaiProduct, max_amount: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_max_amount(
product: &mut RhaiProduct,
max_amount: i64,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.max_amount(max_amount as u16);
Ok(product.clone())
}
#[rhai_fn(name = "purchase_till", return_raw, global, pure)]
pub fn product_purchase_till(product: &mut RhaiProduct, purchase_till: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_purchase_till(
product: &mut RhaiProduct,
purchase_till: i64,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.purchase_till(purchase_till);
Ok(product.clone())
}
#[rhai_fn(name = "active_till", return_raw, global, pure)]
pub fn product_active_till(product: &mut RhaiProduct, active_till: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_active_till(
product: &mut RhaiProduct,
active_till: i64,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.active_till(active_till);
Ok(product.clone())
}
#[rhai_fn(name = "add_component", return_raw, global, pure)]
pub fn product_add_component(product: &mut RhaiProduct, component: RhaiProductComponent) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_add_component(
product: &mut RhaiProduct,
component: RhaiProductComponent,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.add_component(component);
Ok(product.clone())
}
#[rhai_fn(name = "components", return_raw, global, pure)]
pub fn product_components(product: &mut RhaiProduct, components: Vec<RhaiProductComponent>) -> Result<RhaiProduct, Box<EvalAltResult>> {
pub fn product_components(
product: &mut RhaiProduct,
components: Vec<RhaiProductComponent>,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned_product = mem::take(product);
*product = owned_product.components(components);
Ok(product.clone())
}
// Product getters
#[rhai_fn(name = "get_product_id")]
pub fn get_product_id(product: &mut RhaiProduct) -> i64 {
product.get_id() as i64
}
#[rhai_fn(name = "get_product_name")]
pub fn get_product_name(product: &mut RhaiProduct) -> String {
product.name.clone()
}
#[rhai_fn(name = "get_product_description")]
pub fn get_product_description(product: &mut RhaiProduct) -> String {
product.description.clone()
}
#[rhai_fn(name = "get_product_price")]
pub fn get_product_price(product: &mut RhaiProduct) -> f64 {
product.price
}
#[rhai_fn(name = "get_product_type")]
pub fn get_product_type(product: &mut RhaiProduct) -> ProductType {
product.type_.clone()
}
#[rhai_fn(name = "get_product_category")]
pub fn get_product_category(product: &mut RhaiProduct) -> String {
product.category.clone()
}
#[rhai_fn(name = "get_product_status")]
pub fn get_product_status(product: &mut RhaiProduct) -> ProductStatus {
product.status.clone()
}
#[rhai_fn(name = "get_product_max_amount")]
pub fn get_product_max_amount(product: &mut RhaiProduct) -> i64 {
product.max_amount as i64
}
#[rhai_fn(name = "get_product_purchase_till")]
pub fn get_product_purchase_till(product: &mut RhaiProduct) -> i64 {
product.purchase_till
}
#[rhai_fn(name = "get_product_active_till")]
pub fn get_product_active_till(product: &mut RhaiProduct) -> i64 {
product.active_till
}
#[rhai_fn(name = "get_product_components")]
pub fn get_product_components(product: &mut RhaiProduct) -> Vec<RhaiProductComponent> {
product.components.clone()
}
#[rhai_fn(name = "get_product_created_at")]
pub fn get_product_created_at(product: &mut RhaiProduct) -> i64 {
product.base_data.created_at
}
#[rhai_fn(name = "get_product_modified_at")]
pub fn get_product_modified_at(product: &mut RhaiProduct) -> i64 {
product.base_data.modified_at
}
#[rhai_fn(name = "get_product_comments")]
pub fn get_product_comments(product: &mut RhaiProduct) -> Vec<i64> {
product.base_data.comments.iter().map(|&id| id as i64).collect()
product
.base_data
.comments
.iter()
.map(|&id| id as i64)
.collect()
}
// --- SaleItem Functions ---
#[rhai_fn(name = "new_sale_item")]
pub fn new_sale_item() -> RhaiSaleItem {
SaleItem::new()
}
// SaleItem builder methods
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn sale_item_name(item: &mut RhaiSaleItem, name: String) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
pub fn sale_item_name(
item: &mut RhaiSaleItem,
name: String,
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned_item = mem::take(item);
*item = owned_item.name(name);
Ok(item.clone())
}
#[rhai_fn(name = "price", return_raw, global, pure)]
pub fn sale_item_price(item: &mut RhaiSaleItem, price: f64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned_item = mem::take(item);
pub fn sale_item_price(
item: &mut RhaiSaleItem,
price: f64,
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
item.unit_price = price;
Ok(item.clone())
}
#[rhai_fn(name = "quantity", return_raw, global, pure)]
pub fn sale_item_quantity(item: &mut RhaiSaleItem, quantity: i64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
pub fn sale_item_quantity(
item: &mut RhaiSaleItem,
quantity: i64,
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned_item = mem::take(item);
*item = owned_item.quantity(quantity.try_into().unwrap());
Ok(item.clone())
}
#[rhai_fn(name = "product_id", return_raw, global, pure)]
pub fn sale_item_product_id(item: &mut RhaiSaleItem, product_id: i64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
pub fn sale_item_product_id(
item: &mut RhaiSaleItem,
product_id: i64,
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let product_id_u32 = id_from_i64_to_u32(product_id)?;
let owned_item = mem::take(item);
*item = owned_item.product_id(product_id_u32);
Ok(item.clone())
}
// SaleItem getters
#[rhai_fn(name = "get_sale_item_name")]
pub fn get_sale_item_name(item: &mut RhaiSaleItem) -> String {
item.name.clone()
}
#[rhai_fn(name = "get_sale_item_price")]
pub fn get_sale_item_price(item: &mut RhaiSaleItem) -> f64 {
item.unit_price
}
#[rhai_fn(name = "get_sale_item_quantity")]
pub fn get_sale_item_quantity(item: &mut RhaiSaleItem) -> i64 {
item.quantity as i64
}
#[rhai_fn(name = "get_sale_item_product_id")]
pub fn get_sale_item_product_id(item: &mut RhaiSaleItem) -> i64 {
item.product_id as i64
}
// --- Sale Functions ---
#[rhai_fn(name = "new_sale")]
pub fn new_sale() -> RhaiSale {
Sale::new()
}
#[rhai_fn(name = "transaction_id", return_raw, global, pure)]
pub fn sale_transaction_id(sale: &mut RhaiSale, transaction_id: u32) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned_sale = mem::take(sale);
pub fn sale_transaction_id(
sale: &mut RhaiSale,
transaction_id: u32,
) -> Result<RhaiSale, Box<EvalAltResult>> {
sale.transaction_id = transaction_id;
Ok(sale.clone())
}
#[rhai_fn(name = "status", return_raw, global, pure)]
pub fn sale_status(sale: &mut RhaiSale, status: SaleStatus) -> Result<RhaiSale, Box<EvalAltResult>> {
pub fn sale_status(
sale: &mut RhaiSale,
status: SaleStatus,
) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned_sale = mem::take(sale);
*sale = owned_sale.status(status);
Ok(sale.clone())
}
#[rhai_fn(name = "add_item", return_raw, global, pure)]
pub fn sale_add_item(sale: &mut RhaiSale, item: RhaiSaleItem) -> Result<RhaiSale, Box<EvalAltResult>> {
pub fn sale_add_item(
sale: &mut RhaiSale,
item: RhaiSaleItem,
) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned_sale = mem::take(sale);
*sale = owned_sale.add_item(item);
Ok(sale.clone())
}
#[rhai_fn(name = "items", return_raw, global, pure)]
pub fn sale_items(sale: &mut RhaiSale, items: Vec<RhaiSaleItem>) -> Result<RhaiSale, Box<EvalAltResult>> {
pub fn sale_items(
sale: &mut RhaiSale,
items: Vec<RhaiSaleItem>,
) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned_sale = mem::take(sale);
*sale = owned_sale.items(items);
Ok(sale.clone())
@@ -483,35 +581,39 @@ mod rhai_biz_module {
pub fn get_sale_id(sale: &mut RhaiSale) -> i64 {
sale.get_id() as i64
}
#[rhai_fn(name = "get_sale_transaction_id")]
pub fn get_sale_transaction_id(sale: &mut RhaiSale) -> u32 {
sale.transaction_id
}
#[rhai_fn(name = "get_sale_status")]
pub fn get_sale_status(sale: &mut RhaiSale) -> SaleStatus {
sale.status.clone()
}
#[rhai_fn(name = "get_sale_items")]
pub fn get_sale_items(sale: &mut RhaiSale) -> Vec<RhaiSaleItem> {
sale.items.clone()
}
#[rhai_fn(name = "get_sale_created_at")]
pub fn get_sale_created_at(sale: &mut RhaiSale) -> i64 {
sale.base_data.created_at
}
#[rhai_fn(name = "get_sale_modified_at")]
pub fn get_sale_modified_at(sale: &mut RhaiSale) -> i64 {
sale.base_data.modified_at
}
#[rhai_fn(name = "get_sale_comments")]
pub fn get_sale_comments(sale: &mut RhaiSale) -> Vec<i64> {
sale.base_data.comments.iter().map(|&id| id as i64).collect()
sale.base_data
.comments
.iter()
.map(|&id| id as i64)
.collect()
}
}
@@ -519,110 +621,174 @@ pub fn register_biz_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_biz_module);
engine.register_global_module(module.into());
// Create a new module for database operations
let mut db_module = Module::new();
// Database operations will obtain fresh collection handles directly.
// Add database functions for Company
let db_for_set_company = Arc::clone(&db);
db_module.set_native_fn("set_company", move |company: Company| -> Result<INT, Box<EvalAltResult>> {
let company_collection_set = db_for_set_company.collection::<Company>().expect("Failed to get company collection for set in closure");
company_collection_set.set(&company)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save company: {:?}", e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"set_company",
move |company: Company| -> Result<INT, Box<EvalAltResult>> {
let company_collection_set = db_for_set_company
.collection::<Company>()
.expect("Failed to get company collection for set in closure");
company_collection_set
.set(&company)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save company: {:?}", e).into(),
Position::NONE,
))
})
},
);
let db_for_get_company = Arc::clone(&db);
db_module.set_native_fn("get_company_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let company_collection_get = db_for_get_company.collection::<Company>().expect("Failed to get company collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
company_collection_get.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get company with id {}: {:?}", id, e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"get_company_by_id",
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let company_collection_get = db_for_get_company
.collection::<Company>()
.expect("Failed to get company collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
company_collection_get
.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get company with id {}: {:?}", id, e).into(),
Position::NONE,
))
})
},
);
// Add database functions for Shareholder
let db_for_set_shareholder = Arc::clone(&db);
db_module.set_native_fn("set_shareholder", move |shareholder: Shareholder| -> Result<INT, Box<EvalAltResult>> {
let shareholder_collection_set = db_for_set_shareholder.collection::<Shareholder>().expect("Failed to get shareholder collection for set in closure");
shareholder_collection_set.set(&shareholder)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save shareholder: {:?}", e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"set_shareholder",
move |shareholder: Shareholder| -> Result<INT, Box<EvalAltResult>> {
let shareholder_collection_set = db_for_set_shareholder
.collection::<Shareholder>()
.expect("Failed to get shareholder collection for set in closure");
shareholder_collection_set
.set(&shareholder)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save shareholder: {:?}", e).into(),
Position::NONE,
))
})
},
);
let db_for_get_shareholder = Arc::clone(&db);
db_module.set_native_fn("get_shareholder_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let shareholder_collection_get = db_for_get_shareholder.collection::<Shareholder>().expect("Failed to get shareholder collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
shareholder_collection_get.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get shareholder with id {}: {:?}", id, e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"get_shareholder_by_id",
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let shareholder_collection_get = db_for_get_shareholder
.collection::<Shareholder>()
.expect("Failed to get shareholder collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
shareholder_collection_get
.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get shareholder with id {}: {:?}", id, e).into(),
Position::NONE,
))
})
},
);
// Add database functions for Product
let db_for_set_product = Arc::clone(&db);
db_module.set_native_fn("set_product", move |product: Product| -> Result<INT, Box<EvalAltResult>> {
let product_collection_set = db_for_set_product.collection::<Product>().expect("Failed to get product collection for set in closure");
product_collection_set.set(&product)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save product: {:?}", e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"set_product",
move |product: Product| -> Result<INT, Box<EvalAltResult>> {
let product_collection_set = db_for_set_product
.collection::<Product>()
.expect("Failed to get product collection for set in closure");
product_collection_set
.set(&product)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save product: {:?}", e).into(),
Position::NONE,
))
})
},
);
let db_for_get_product = Arc::clone(&db);
db_module.set_native_fn("get_product_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let product_collection_get = db_for_get_product.collection::<Product>().expect("Failed to get product collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
product_collection_get.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get product with id {}: {:?}", id, e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"get_product_by_id",
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let product_collection_get = db_for_get_product
.collection::<Product>()
.expect("Failed to get product collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
product_collection_get
.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get product with id {}: {:?}", id, e).into(),
Position::NONE,
))
})
},
);
// Add database functions for Sale
let db_for_set_sale = Arc::clone(&db);
db_module.set_native_fn("set_sale", move |sale: Sale| -> Result<INT, Box<EvalAltResult>> {
let sale_collection_set = db_for_set_sale.collection::<Sale>().expect("Failed to get sale collection for set in closure");
sale_collection_set.set(&sale)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save sale: {:?}", e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"set_sale",
move |sale: Sale| -> Result<INT, Box<EvalAltResult>> {
let sale_collection_set = db_for_set_sale
.collection::<Sale>()
.expect("Failed to get sale collection for set in closure");
sale_collection_set
.set(&sale)
.map(|(id_val, _)| id_val as INT)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save sale: {:?}", e).into(),
Position::NONE,
))
})
},
);
let db_for_get_sale = Arc::clone(&db);
db_module.set_native_fn("get_sale_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let sale_collection_get = db_for_get_sale.collection::<Sale>().expect("Failed to get sale collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
sale_collection_get.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get sale with id {}: {:?}", id, e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"get_sale_by_id",
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let sale_collection_get = db_for_get_sale
.collection::<Sale>()
.expect("Failed to get sale collection for get in closure");
let id_u32 = id_from_i64_to_u32(id)?;
sale_collection_get
.get_by_id(id_u32)
.map(Dynamic::from)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get sale with id {}: {:?}", id, e).into(),
Position::NONE,
))
})
},
);
// Register the database module globally
engine.register_global_module(db_module.into());
println!("Successfully registered biz Rhai module using export_module approach.");
}

View File

@@ -1,5 +1,5 @@
use heromodels_core::{BaseModelData, BaseModelDataOps, Model};
use serde::{Deserialize, Serialize};
use heromodels_core::{BaseModelData, Model, BaseModelDataOps};
/// Represents the status of a sale.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ShareholderType {
@@ -31,13 +31,13 @@ impl Shareholder {
pub fn new() -> Self {
Self {
base_data: BaseModelData::new(),
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
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
since: 0, // Default timestamp, to be set by builder
}
}
@@ -78,4 +78,4 @@ impl Shareholder {
}
// Base data operations are now handled by BaseModelDataOps trait
}
}

View File

@@ -26,7 +26,7 @@ impl AttendanceStatus {
_ => Err(format!("Invalid attendance status: '{}'", s)),
}
}
/// Convert an AttendanceStatus to a string
pub fn to_string(&self) -> String {
match self {
@@ -134,7 +134,11 @@ impl Event {
/// Adds an attendee to the event
pub fn add_attendee(mut self, attendee: Attendee) -> Self {
// Prevent duplicate attendees by contact_id
if !self.attendees.iter().any(|a| a.contact_id == attendee.contact_id) {
if !self
.attendees
.iter()
.any(|a| a.contact_id == attendee.contact_id)
{
self.attendees.push(attendee);
}
self
@@ -148,18 +152,18 @@ impl Event {
/// Updates the status of an existing attendee
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) {
if let Some(attendee) = self
.attendees
.iter_mut()
.find(|a| a.contact_id == contact_id)
{
attendee.status = status;
}
self
}
/// Reschedules the event to new start and end times
pub fn reschedule(
mut self,
new_start_time: i64,
new_end_time: i64,
) -> Self {
pub fn reschedule(mut self, new_start_time: i64, new_end_time: i64) -> Self {
// Basic validation: end_time should be after start_time
if new_end_time > new_start_time {
self.start_time = new_start_time;
@@ -236,7 +240,8 @@ impl Calendar {
/// Removes an event from the calendar by its ID
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.events
.retain(|&event_id_in_vec| event_id_in_vec != event_id_to_remove);
self
}
}

View File

@@ -3,5 +3,5 @@ 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 self::calendar::{AttendanceStatus, Attendee, Calendar, Event};
pub use rhai::register_calendar_rhai_module;

View File

@@ -1,24 +1,24 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use super::calendar::{Event, Attendee, Calendar, AttendanceStatus};
use super::calendar::{AttendanceStatus, Attendee, Calendar, Event};
type RhaiEvent = Event;
type RhaiAttendee = Attendee;
type RhaiCalendar = Calendar;
use crate::db::hero::OurDB;
use crate::db::Collection;
use crate::db::hero::OurDB;
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
u32::try_from(id_i64).map_err(|_|
u32::try_from(id_i64).map_err(|_| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert ID '{}' to u32", id_i64).into(),
Position::NONE
Position::NONE,
))
)
})
}
#[export_module]
@@ -28,26 +28,35 @@ mod rhai_calendar_module {
pub fn new_event() -> RhaiEvent {
Event::new()
}
/// Sets the event title
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn event_title(event: &mut RhaiEvent, title: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
pub fn event_title(
event: &mut RhaiEvent,
title: String,
) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned_event = mem::take(event);
*event = owned_event.title(title);
Ok(event.clone())
}
/// Sets the event description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn event_description(event: &mut RhaiEvent, description: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
pub fn event_description(
event: &mut RhaiEvent,
description: String,
) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned_event = mem::take(event);
*event = owned_event.description(description);
Ok(event.clone())
}
/// Sets the event location
#[rhai_fn(name = "location", return_raw, global, pure)]
pub fn event_location(event: &mut RhaiEvent, location: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
pub fn event_location(
event: &mut RhaiEvent,
location: String,
) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned_event = mem::take(event);
*event = owned_event.location(location);
Ok(event.clone())
@@ -55,7 +64,10 @@ mod rhai_calendar_module {
/// Adds an attendee to the event
#[rhai_fn(name = "add_attendee", return_raw, global, pure)]
pub fn event_add_attendee(event: &mut RhaiEvent, attendee: RhaiAttendee) -> Result<RhaiEvent, Box<EvalAltResult>> {
pub fn event_add_attendee(
event: &mut RhaiEvent,
attendee: RhaiAttendee,
) -> Result<RhaiEvent, Box<EvalAltResult>> {
// Use take to get ownership of the event
let owned_event = mem::take(event);
*event = owned_event.add_attendee(attendee);
@@ -64,30 +76,38 @@ mod rhai_calendar_module {
/// Reschedules the event with new start and end times
#[rhai_fn(name = "reschedule", return_raw, global, pure)]
pub fn event_reschedule(event: &mut RhaiEvent, new_start_time: i64, new_end_time: i64) -> Result<RhaiEvent, Box<EvalAltResult>> {
pub fn event_reschedule(
event: &mut RhaiEvent,
new_start_time: i64,
new_end_time: i64,
) -> Result<RhaiEvent, Box<EvalAltResult>> {
// Validate timestamps
if new_end_time <= new_start_time {
return Err(Box::new(EvalAltResult::ErrorRuntime(
"End time must be after start time".into(),
Position::NONE
Position::NONE,
)));
}
// Use take to get ownership of the event
let owned_event = mem::take(event);
*event = owned_event.reschedule(new_start_time, new_end_time);
Ok(event.clone())
}
/// Updates an attendee's status in the event
#[rhai_fn(name = "update_attendee_status", return_raw, global, pure)]
pub fn event_update_attendee_status(event: &mut RhaiEvent, contact_id: i64, status_str: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
pub fn event_update_attendee_status(
event: &mut RhaiEvent,
contact_id: i64,
status_str: String,
) -> Result<RhaiEvent, Box<EvalAltResult>> {
let status_enum = AttendanceStatus::from_string(&status_str)
.map_err(|_| Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid attendance status: '{}'. Expected one of: Pending, Accepted, Declined, Tentative", status_str).into(),
Position::NONE
)))?;
// Use take to get ownership of the event
let owned_event = mem::take(event);
*event = owned_event.update_attendee_status(id_from_i64_to_u32(contact_id)?, status_enum);
@@ -96,50 +116,74 @@ mod rhai_calendar_module {
// Event Getters
#[rhai_fn(get = "id", pure)]
pub fn get_event_id(event: &mut RhaiEvent) -> i64 { event.base_data.id as i64 }
pub fn get_event_id(event: &mut RhaiEvent) -> i64 {
event.base_data.id as i64
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_event_created_at(event: &mut RhaiEvent) -> i64 { event.base_data.created_at }
pub fn get_event_created_at(event: &mut RhaiEvent) -> i64 {
event.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_event_modified_at(event: &mut RhaiEvent) -> i64 { event.base_data.modified_at }
pub fn get_event_modified_at(event: &mut RhaiEvent) -> i64 {
event.base_data.modified_at
}
#[rhai_fn(get = "title", pure)]
pub fn get_event_title(event: &mut RhaiEvent) -> String { event.title.clone() }
pub fn get_event_title(event: &mut RhaiEvent) -> String {
event.title.clone()
}
#[rhai_fn(get = "description", pure)]
pub fn get_event_description(event: &mut RhaiEvent) -> Option<String> { event.description.clone() }
pub fn get_event_description(event: &mut RhaiEvent) -> Option<String> {
event.description.clone()
}
#[rhai_fn(get = "start_time", pure)]
pub fn get_event_start_time(event: &mut RhaiEvent) -> i64 { event.start_time }
pub fn get_event_start_time(event: &mut RhaiEvent) -> i64 {
event.start_time
}
#[rhai_fn(get = "end_time", pure)]
pub fn get_event_end_time(event: &mut RhaiEvent) -> i64 { event.end_time }
pub fn get_event_end_time(event: &mut RhaiEvent) -> i64 {
event.end_time
}
#[rhai_fn(get = "location", pure)]
pub fn get_event_location(event: &mut RhaiEvent) -> Option<String> { event.location.clone() }
pub fn get_event_location(event: &mut RhaiEvent) -> Option<String> {
event.location.clone()
}
#[rhai_fn(get = "attendees", pure)]
pub fn get_event_attendees(event: &mut RhaiEvent) -> Vec<RhaiAttendee> { event.attendees.clone() }
pub fn get_event_attendees(event: &mut RhaiEvent) -> Vec<RhaiAttendee> {
event.attendees.clone()
}
// --- Attendee Functions ---
#[rhai_fn(name = "new_attendee")]
pub fn new_attendee() -> RhaiAttendee {
Attendee::new(0) // Default contact_id, will be set via builder
}
/// Sets the contact ID for an attendee
#[rhai_fn(name = "with_contact_id", return_raw, global, pure)]
pub fn attendee_with_contact_id(attendee: &mut RhaiAttendee, contact_id: i64) -> Result<RhaiAttendee, Box<EvalAltResult>> {
pub fn attendee_with_contact_id(
attendee: &mut RhaiAttendee,
contact_id: i64,
) -> Result<RhaiAttendee, Box<EvalAltResult>> {
let new_contact_id = id_from_i64_to_u32(contact_id).unwrap_or(0);
let owned_attendee = mem::replace(attendee, Attendee::new(0));
*attendee = Attendee::new(new_contact_id);
attendee.status = owned_attendee.status;
Ok(attendee.clone())
}
/// Sets the status for an attendee
#[rhai_fn(name = "with_status", return_raw, global, pure)]
pub fn attendee_with_status(attendee: &mut RhaiAttendee, status_str: String) -> Result<RhaiAttendee, Box<EvalAltResult>> {
pub fn attendee_with_status(
attendee: &mut RhaiAttendee,
status_str: String,
) -> Result<RhaiAttendee, Box<EvalAltResult>> {
let status_enum = AttendanceStatus::from_string(&status_str)
.map_err(|_| Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid attendance status: '{}'. Expected one of: Accepted, Declined, Tentative, NoResponse", status_str).into(),
Position::NONE
)))?;
let owned_attendee = mem::replace(attendee, Attendee::new(0));
*attendee = owned_attendee.status(status_enum);
Ok(attendee.clone())
@@ -149,9 +193,13 @@ mod rhai_calendar_module {
// Attendee Getters
#[rhai_fn(get = "contact_id", pure)]
pub fn get_attendee_contact_id(attendee: &mut RhaiAttendee) -> i64 { attendee.contact_id as i64 }
pub fn get_attendee_contact_id(attendee: &mut RhaiAttendee) -> i64 {
attendee.contact_id as i64
}
#[rhai_fn(get = "status", pure)]
pub fn get_attendee_status(attendee: &mut RhaiAttendee) -> String { attendee.status.to_string() }
pub fn get_attendee_status(attendee: &mut RhaiAttendee) -> String {
attendee.status.to_string()
}
// --- Calendar Functions ---
#[rhai_fn(name = "new_calendar", return_raw)]
@@ -159,25 +207,31 @@ mod rhai_calendar_module {
let calendar = Calendar::new(None, "");
Ok(calendar)
}
/// Sets the calendar name
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn calendar_name(calendar: &mut RhaiCalendar, name: String) -> Result<RhaiCalendar, Box<EvalAltResult>> {
pub fn calendar_name(
calendar: &mut RhaiCalendar,
name: String,
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
// Create a default Calendar to replace the taken one
let default_calendar = Calendar::new(None, "");
// Take ownership of the calendar, apply the builder method, then put it back
let owned_calendar = std::mem::replace(calendar, default_calendar);
*calendar = Calendar::new(Some(owned_calendar.base_data.id), name);
Ok(calendar.clone())
}
/// Sets the calendar description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn calendar_description(calendar: &mut RhaiCalendar, description: String) -> Result<RhaiCalendar, Box<EvalAltResult>> {
pub fn calendar_description(
calendar: &mut RhaiCalendar,
description: String,
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
// Create a default Calendar to replace the taken one
let default_calendar = Calendar::new(None, "");
// Take ownership of the calendar, apply the builder method, then put it back
let owned_calendar = std::mem::replace(calendar, default_calendar);
let updated_calendar = owned_calendar.description(description);
@@ -186,10 +240,13 @@ mod rhai_calendar_module {
}
#[rhai_fn(name = "add_event_to_calendar", return_raw, global, pure)]
pub fn calendar_add_event(calendar: &mut RhaiCalendar, event: RhaiEvent) -> Result<RhaiCalendar, Box<EvalAltResult>> {
pub fn calendar_add_event(
calendar: &mut RhaiCalendar,
event: RhaiEvent,
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
// Create a default Calendar to replace the taken one
let default_calendar = Calendar::new(None, "");
// Take ownership of the calendar, apply the builder method, then put it back
let owned_calendar = std::mem::replace(calendar, default_calendar);
*calendar = owned_calendar.add_event(event.base_data.id as i64);
@@ -197,10 +254,13 @@ mod rhai_calendar_module {
}
#[rhai_fn(name = "remove_event_from_calendar", return_raw)]
pub fn calendar_remove_event(calendar: &mut RhaiCalendar, event_id: i64) -> Result<(), Box<EvalAltResult>> {
pub fn calendar_remove_event(
calendar: &mut RhaiCalendar,
event_id: i64,
) -> Result<(), Box<EvalAltResult>> {
// Create a default Calendar to replace the taken one
let default_calendar = Calendar::new(None, "");
// Take ownership of the calendar, apply the builder method, then put it back
let owned_calendar = std::mem::replace(calendar, default_calendar);
*calendar = owned_calendar.remove_event(id_from_i64_to_u32(event_id)? as i64);
@@ -209,140 +269,213 @@ mod rhai_calendar_module {
// Calendar Getters
#[rhai_fn(get = "id", pure)]
pub fn get_calendar_id(calendar: &mut RhaiCalendar) -> i64 { calendar.base_data.id as i64 }
pub fn get_calendar_id(calendar: &mut RhaiCalendar) -> i64 {
calendar.base_data.id as i64
}
#[rhai_fn(get = "name", pure)]
pub fn get_calendar_name(calendar: &mut RhaiCalendar) -> String { calendar.name.clone() }
pub fn get_calendar_name(calendar: &mut RhaiCalendar) -> String {
calendar.name.clone()
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_calendar_created_at(calendar: &mut RhaiCalendar) -> i64 { calendar.base_data.created_at }
pub fn get_calendar_created_at(calendar: &mut RhaiCalendar) -> i64 {
calendar.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_calendar_modified_at(calendar: &mut RhaiCalendar) -> i64 { calendar.base_data.modified_at }
pub fn get_calendar_modified_at(calendar: &mut RhaiCalendar) -> i64 {
calendar.base_data.modified_at
}
#[rhai_fn(get = "events", pure)]
pub fn get_calendar_events(calendar: &mut RhaiCalendar) -> Vec<i64> { calendar.events.clone() }
pub fn get_calendar_events(calendar: &mut RhaiCalendar) -> Vec<i64> {
calendar.events.clone()
}
#[rhai_fn(get = "description", pure)]
pub fn get_calendar_description(calendar: &mut RhaiCalendar) -> Option<String> { calendar.description.clone() }
pub fn get_calendar_description(calendar: &mut RhaiCalendar) -> Option<String> {
calendar.description.clone()
}
// Calendar doesn't have an owner_id field in the current implementation
// pub fn get_calendar_owner_id(calendar: &mut RhaiCalendar) -> i64 { calendar.owner_id as i64 }
}
pub fn register_calendar_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_calendar_module);
engine.register_global_module(module.into());
// Create a module for database functions
let mut db_module = Module::new();
// Manually register database functions as they need to capture 'db'
let db_clone_set_event = db.clone();
db_module.set_native_fn("save_event", move |event: Event| -> Result<Event, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_event.set(&event)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_event: {}", e).into(), Position::NONE)))?;
// Return the updated event with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_event",
move |event: Event| -> Result<Event, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_event.set(&event).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error set_event: {}", e).into(),
Position::NONE,
))
})?;
// Return the updated event with the correct ID
Ok(result.1)
},
);
// Manually register database functions as they need to capture 'db'
let db_clone_delete_event = db.clone();
db_module.set_native_fn("delete_event", move |event: Event| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_event.collection::<Event>()
.expect("can open event collection")
.delete_by_id(event.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
});
db_module.set_native_fn(
"delete_event",
move |event: Event| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_event
.collection::<Event>()
.expect("can open event collection")
.delete_by_id(event.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
},
);
let db_clone_get_event = db.clone();
db_module.set_native_fn("get_event_by_id", move |id_i64: INT| -> Result<Event, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_event.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_event_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Event with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_event_by_id",
move |id_i64: INT| -> Result<Event, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_event
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_event_by_id: {}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Event with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
let db_clone_set_calendar = db.clone();
db_module.set_native_fn("save_calendar", move |calendar: Calendar| -> Result<Calendar, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_calendar.set(&calendar)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_calendar: {}", e).into(), Position::NONE)))?;
// Return the updated calendar with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_calendar",
move |calendar: Calendar| -> Result<Calendar, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_calendar.set(&calendar).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error set_calendar: {}", e).into(),
Position::NONE,
))
})?;
// Return the updated calendar with the correct ID
Ok(result.1)
},
);
// Manually register database functions as they need to capture 'db'
let db_clone_delete_calendar = db.clone();
db_module.set_native_fn("delete_calendar", move |calendar: Calendar| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_calendar.collection::<Calendar>()
.expect("can open calendar collection")
.delete_by_id(calendar.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
});
db_module.set_native_fn(
"delete_calendar",
move |calendar: Calendar| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_calendar
.collection::<Calendar>()
.expect("can open calendar collection")
.delete_by_id(calendar.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
},
);
let db_clone_get_calendar = db.clone();
db_module.set_native_fn("get_calendar_by_id", move |id_i64: INT| -> Result<Calendar, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_calendar.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_calendar_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Calendar with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_calendar_by_id",
move |id_i64: INT| -> Result<Calendar, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_calendar
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_calendar_by_id: {}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Calendar with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
// Add list_calendars function to get all calendars
let db_clone_list_calendars = db.clone();
db_module.set_native_fn("list_calendars", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_calendars.collection::<Calendar>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get calendar collection: {:?}", e).into(),
Position::NONE
)))?;
let calendars = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all calendars: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for calendar in calendars {
array.push(Dynamic::from(calendar));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_calendars",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_calendars
.collection::<Calendar>()
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get calendar collection: {:?}", e).into(),
Position::NONE,
))
})?;
let calendars = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all calendars: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for calendar in calendars {
array.push(Dynamic::from(calendar));
}
Ok(Dynamic::from(array))
},
);
// Add list_events function to get all events
let db_clone_list_events = db.clone();
db_module.set_native_fn("list_events", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_events.collection::<Event>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get event collection: {:?}", e).into(),
Position::NONE
)))?;
let events = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all events: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for event in events {
array.push(Dynamic::from(event));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_events",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_events.collection::<Event>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get event collection: {:?}", e).into(),
Position::NONE,
))
})?;
let events = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all events: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for event in events {
array.push(Dynamic::from(event));
}
Ok(Dynamic::from(array))
},
);
// Register the database module globally
engine.register_global_module(db_module.into());

View File

@@ -19,6 +19,8 @@ pub struct Circle {
pub description: Option<String>,
/// List of related circles
pub circles: Vec<String>,
/// List of members in the circle (their public keys)
pub members: Vec<String>,
/// Logo URL or symbol for the circle
pub logo: Option<String>,
/// Theme settings for the circle (colors, styling, etc.)
@@ -35,6 +37,7 @@ impl Circle {
description: None,
circles: Vec::new(),
logo: None,
members: Vec::new(),
theme: HashMap::new(),
}
}
@@ -83,4 +86,13 @@ impl Circle {
}
self
}
}
/// Adds a member to the circle
pub fn add_member(mut self, member: String) -> Self {
// Prevent duplicate members
if !self.members.iter().any(|a| *a == member) {
self.members.push(member);
}
self
}
}

View File

@@ -3,5 +3,5 @@ pub mod circle;
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::circle::{Circle};
pub use self::circle::Circle;
pub use rhai::register_circle_rhai_module;

View File

@@ -1,16 +1,16 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array, CustomType};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use rhai::plugin::*;
use rhai::{Array, CustomType, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use super::circle::{Circle};
use super::circle::Circle;
type RhaiCircle = Circle;
use crate::db::hero::OurDB;
use crate::db::Collection;
use crate::db::hero::OurDB;
use serde::Serialize;
use std::collections::HashMap;
use serde_json;
use std::collections::HashMap;
/// Registers a `.json()` method for any type `T` that implements the required traits.
fn register_json_method<T>(engine: &mut Engine)
@@ -31,12 +31,12 @@ where
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
u32::try_from(id_i64).map_err(|_|
u32::try_from(id_i64).map_err(|_| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert ID '{}' to u32", id_i64).into(),
Position::NONE
Position::NONE,
))
)
})
}
#[export_module]
@@ -46,10 +46,13 @@ mod rhai_circle_module {
pub fn new_circle() -> RhaiCircle {
Circle::new()
}
/// Sets the circle title
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn circle_title(circle: &mut RhaiCircle, title: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
pub fn circle_title(
circle: &mut RhaiCircle,
title: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.title(title);
Ok(circle.clone())
@@ -57,15 +60,21 @@ mod rhai_circle_module {
/// Sets the circle ws_url
#[rhai_fn(name = "ws_url", return_raw, global, pure)]
pub fn circle_ws_url(circle: &mut RhaiCircle, ws_url: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
pub fn circle_ws_url(
circle: &mut RhaiCircle,
ws_url: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.ws_url(ws_url);
Ok(circle.clone())
}
/// Sets the circle description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn circle_description(circle: &mut RhaiCircle, description: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
pub fn circle_description(
circle: &mut RhaiCircle,
description: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.description(description);
Ok(circle.clone())
@@ -73,7 +82,10 @@ mod rhai_circle_module {
/// Sets the circle logo
#[rhai_fn(name = "logo", return_raw, global, pure)]
pub fn circle_logo(circle: &mut RhaiCircle, logo: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
pub fn circle_logo(
circle: &mut RhaiCircle,
logo: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.logo(logo);
Ok(circle.clone())
@@ -81,118 +93,194 @@ mod rhai_circle_module {
/// Sets the circle theme
#[rhai_fn(name = "theme", return_raw, global, pure)]
pub fn circle_theme(circle: &mut RhaiCircle, theme: HashMap<String, String>) -> Result<RhaiCircle, Box<EvalAltResult>> {
pub fn circle_theme(
circle: &mut RhaiCircle,
theme: HashMap<String, String>,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.theme(theme);
Ok(circle.clone())
}
/// Adds an attendee to the circle
#[rhai_fn(name = "add_circle", return_raw, global, pure)]
pub fn circle_add_circle(circle: &mut RhaiCircle, added_circle: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
pub fn circle_add_circle(
circle: &mut RhaiCircle,
added_circle: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
// Use take to get ownership of the circle
let owned_circle = mem::take(circle);
*circle = owned_circle.add_circle(added_circle);
Ok(circle.clone())
}
/// Adds an attendee to the circle
#[rhai_fn(name = "add_member", return_raw, global, pure)]
pub fn circle_add_member(
circle: &mut RhaiCircle,
added_member: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
// Use take to get ownership of the circle
let owned_circle = mem::take(circle);
*circle = owned_circle.add_member(added_member);
Ok(circle.clone())
}
// Circle Getters
#[rhai_fn(get = "id", pure)]
pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 { circle.base_data.id as i64 }
pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 {
circle.base_data.id as i64
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 { circle.base_data.created_at }
pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 {
circle.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 { circle.base_data.modified_at }
pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 {
circle.base_data.modified_at
}
#[rhai_fn(get = "title", pure)]
pub fn get_circle_title(circle: &mut RhaiCircle) -> String { circle.title.clone() }
pub fn get_circle_title(circle: &mut RhaiCircle) -> String {
circle.title.clone()
}
#[rhai_fn(get = "description", pure)]
pub fn get_circle_description(circle: &mut RhaiCircle) -> Option<String> { circle.description.clone() }
pub fn get_circle_description(circle: &mut RhaiCircle) -> Option<String> {
circle.description.clone()
}
#[rhai_fn(get = "circles", pure)]
pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec<String> { circle.circles.clone() }
pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec<String> {
circle.circles.clone()
}
#[rhai_fn(get = "ws_url", pure)]
pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String { circle.ws_url.clone() }
pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String {
circle.ws_url.clone()
}
#[rhai_fn(get = "logo", pure)]
pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option<String> { circle.logo.clone() }
pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option<String> {
circle.logo.clone()
}
#[rhai_fn(get = "theme", pure)]
pub fn get_circle_theme(circle: &mut RhaiCircle) -> HashMap<String, String> { circle.theme.clone() }
pub fn get_circle_theme(circle: &mut RhaiCircle) -> HashMap<String, String> {
circle.theme.clone()
}
}
pub fn register_circle_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_circle_module);
engine.register_global_module(module.into());
// Create a module for database functions
let mut db_module = Module::new();
// Manually register database functions as they need to capture 'db'
let db_clone_set_circle = db.clone();
db_module.set_native_fn("save_circle", move |circle: Circle| -> Result<Circle, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_circle.set(&circle)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_circle: {}", e).into(), Position::NONE)))?;
// Return the updated circle with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_circle",
move |circle: Circle| -> Result<Circle, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_circle.set(&circle).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error set_circle: {}", e).into(),
Position::NONE,
))
})?;
// Return the updated circle with the correct ID
Ok(result.1)
},
);
register_json_method::<Circle>(engine);
// Manually register database functions as they need to capture 'db'
let db_clone_delete_circle = db.clone();
db_module.set_native_fn("delete_circle", move |circle: Circle| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_circle.collection::<Circle>()
.expect("can open circle collection")
.delete_by_id(circle.base_data.id)
.expect("can delete circle");
// Return the updated circle with the correct ID
Ok(result)
});
let db_clone_get_circle = db.clone();
db_module.set_native_fn("get_circle", move || -> Result<Circle, Box<EvalAltResult>> {
// Use the Collection trait method directly
let all_circles: Vec<Circle> = db_clone_get_circle.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_circle: {}", e).into(), Position::NONE)))?;
db_module.set_native_fn(
"delete_circle",
move |circle: Circle| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_circle
.collection::<Circle>()
.expect("can open circle collection")
.delete_by_id(circle.base_data.id)
.expect("can delete circle");
if let Some(first_circle) = all_circles.first() {
Ok(first_circle.clone())
} else {
Err(Box::new(EvalAltResult::ErrorRuntime("Circle not found".into(), Position::NONE)))
}
});
// Return the updated circle with the correct ID
Ok(result)
},
);
let db_clone_get_circle = db.clone();
db_module.set_native_fn(
"get_circle",
move || -> Result<Circle, Box<EvalAltResult>> {
// Use the Collection trait method directly
let all_circles: Vec<Circle> = db_clone_get_circle.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_circle: {}", e).into(),
Position::NONE,
))
})?;
if let Some(first_circle) = all_circles.first() {
Ok(first_circle.clone())
} else {
Err(Box::new(EvalAltResult::ErrorRuntime(
"Circle not found".into(),
Position::NONE,
)))
}
},
);
let db_clone_get_circle_by_id = db.clone();
db_module.set_native_fn("get_circle_by_id", move |id_i64: INT| -> Result<Circle, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_circle_by_id.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_circle_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Circle with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_circle_by_id",
move |id_i64: INT| -> Result<Circle, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_circle_by_id
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_circle_by_id: {}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Circle with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
// Add list_circles function to get all circles
let db_clone_list_circles = db.clone();
db_module.set_native_fn("list_circles", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_circles.collection::<Circle>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get circle collection: {:?}", e).into(),
Position::NONE
)))?;
let circles = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all circles: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for circle in circles {
array.push(Dynamic::from(circle));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_circles",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_circles.collection::<Circle>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get circle collection: {:?}", e).into(),
Position::NONE,
))
})?;
let circles = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all circles: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for circle in circles {
array.push(Dynamic::from(circle));
}
Ok(Dynamic::from(array))
},
);
// Register the database module globally
engine.register_global_module(db_module.into());

View File

@@ -112,4 +112,4 @@ impl Group {
self.contacts.push(contact);
self
}
}
}

View File

@@ -1,23 +1,23 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use super::contact::{Group, Contact};
use super::contact::{Contact, Group};
type RhaiGroup = Group;
type RhaiContact = Contact;
use crate::db::hero::OurDB;
use crate::db::Collection;
use crate::db::hero::OurDB;
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
u32::try_from(id_i64).map_err(|_|
u32::try_from(id_i64).map_err(|_| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert ID '{}' to u32", id_i64).into(),
Position::NONE
Position::NONE,
))
)
})
}
#[export_module]
@@ -27,18 +27,24 @@ mod rhai_contact_module {
pub fn new_group() -> RhaiGroup {
Group::new()
}
/// Sets the event title
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn group_name(group: &mut RhaiGroup, name: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
pub fn group_name(
group: &mut RhaiGroup,
name: String,
) -> Result<RhaiGroup, Box<EvalAltResult>> {
let owned_group = mem::take(group);
*group = owned_group.name(name);
Ok(group.clone())
}
/// Sets the event description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn group_description(group: &mut RhaiGroup, description: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
pub fn group_description(
group: &mut RhaiGroup,
description: String,
) -> Result<RhaiGroup, Box<EvalAltResult>> {
let owned_group = mem::take(group);
*group = owned_group.description(description);
Ok(group.clone())
@@ -46,7 +52,10 @@ mod rhai_contact_module {
/// Adds an attendee to the event
#[rhai_fn(name = "add_contact", return_raw, global, pure)]
pub fn group_add_contact(group: &mut RhaiGroup, contact_id: i64) -> Result<RhaiGroup, Box<EvalAltResult>> {
pub fn group_add_contact(
group: &mut RhaiGroup,
contact_id: i64,
) -> Result<RhaiGroup, Box<EvalAltResult>> {
// Use take to get ownership of the event
let owned_group = mem::take(group);
*group = owned_group.add_contact(contact_id as u32);
@@ -54,21 +63,37 @@ mod rhai_contact_module {
}
#[rhai_fn(get = "contacts", pure)]
pub fn get_group_contacts(group: &mut RhaiGroup) -> Vec<i64> { group.contacts.clone().into_iter().map(|id| id as i64).collect() }
pub fn get_group_contacts(group: &mut RhaiGroup) -> Vec<i64> {
group
.contacts
.clone()
.into_iter()
.map(|id| id as i64)
.collect()
}
// Group Getters
#[rhai_fn(get = "id", pure)]
pub fn get_group_id(group: &mut RhaiGroup) -> i64 { group.base_data.id as i64 }
pub fn get_group_id(group: &mut RhaiGroup) -> i64 {
group.base_data.id as i64
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_group_created_at(group: &mut RhaiGroup) -> i64 { group.base_data.created_at }
pub fn get_group_created_at(group: &mut RhaiGroup) -> i64 {
group.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_group_modified_at(group: &mut RhaiGroup) -> i64 { group.base_data.modified_at }
#[rhai_fn(get = "name", pure)]
pub fn get_group_name(group: &mut RhaiGroup) -> String { group.name.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_group_description(group: &mut RhaiGroup) -> Option<String> { group.description.clone() }
pub fn get_group_modified_at(group: &mut RhaiGroup) -> i64 {
group.base_data.modified_at
}
#[rhai_fn(get = "name", pure)]
pub fn get_group_name(group: &mut RhaiGroup) -> String {
group.name.clone()
}
#[rhai_fn(get = "description", pure)]
pub fn get_group_description(group: &mut RhaiGroup) -> Option<String> {
group.description.clone()
}
// --- Contact Functions ---
#[rhai_fn(name = "new_contact", return_raw)]
@@ -76,25 +101,31 @@ mod rhai_contact_module {
let contact = Contact::new();
Ok(contact)
}
/// Sets the contact name
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn contact_name(contact: &mut RhaiContact, name: String) -> Result<RhaiContact, Box<EvalAltResult>> {
pub fn contact_name(
contact: &mut RhaiContact,
name: String,
) -> Result<RhaiContact, Box<EvalAltResult>> {
// Create a default Contact to replace the taken one
let default_contact = Contact::new();
// Take ownership of the contact, apply the builder method, then put it back
let owned_contact = std::mem::replace(contact, default_contact);
*contact = owned_contact.name(name);
Ok(contact.clone())
}
/// Sets the contact description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn contact_description(contact: &mut RhaiContact, description: String) -> Result<RhaiContact, Box<EvalAltResult>> {
pub fn contact_description(
contact: &mut RhaiContact,
description: String,
) -> Result<RhaiContact, Box<EvalAltResult>> {
// Create a default Contact to replace the taken one
let default_contact = Contact::new();
// Take ownership of the contact, apply the builder method, then put it back
let owned_contact = std::mem::replace(contact, default_contact);
*contact = owned_contact.description(description);
@@ -103,130 +134,200 @@ mod rhai_contact_module {
// Contact Getters
#[rhai_fn(get = "id", pure)]
pub fn get_contact_id(contact: &mut RhaiContact) -> i64 { contact.base_data.id as i64 }
pub fn get_contact_id(contact: &mut RhaiContact) -> i64 {
contact.base_data.id as i64
}
#[rhai_fn(get = "name", pure)]
pub fn get_contact_name(contact: &mut RhaiContact) -> String { contact.name.clone() }
pub fn get_contact_name(contact: &mut RhaiContact) -> String {
contact.name.clone()
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_contact_created_at(contact: &mut RhaiContact) -> i64 { contact.base_data.created_at }
pub fn get_contact_created_at(contact: &mut RhaiContact) -> i64 {
contact.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_contact_modified_at(contact: &mut RhaiContact) -> i64 { contact.base_data.modified_at }
pub fn get_contact_modified_at(contact: &mut RhaiContact) -> i64 {
contact.base_data.modified_at
}
}
pub fn register_contact_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_contact_module);
engine.register_global_module(module.into());
// Create a module for database functions
let mut db_module = Module::new();
// Manually register database functions as they need to capture 'db'
let db_clone_set_group = db.clone();
db_module.set_native_fn("save_group", move |group: Group| -> Result<Group, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_group.set(&group)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_group: {}", e).into(), Position::NONE)))?;
// Return the updated event with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_group",
move |group: Group| -> Result<Group, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_group.set(&group).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error set_group: {}", e).into(),
Position::NONE,
))
})?;
// Return the updated event with the correct ID
Ok(result.1)
},
);
// Manually register database functions as they need to capture 'db'
let db_clone_delete_group = db.clone();
db_module.set_native_fn("delete_group", move |group: Group| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_group.collection::<Group>()
.expect("can open group collection")
.delete_by_id(group.base_data.id)
.expect("can delete group");
// Return the updated event with the correct ID
Ok(result)
});
db_module.set_native_fn(
"delete_group",
move |group: Group| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_group
.collection::<Group>()
.expect("can open group collection")
.delete_by_id(group.base_data.id)
.expect("can delete group");
// Return the updated event with the correct ID
Ok(result)
},
);
let db_clone_get_group = db.clone();
db_module.set_native_fn("get_group_by_id", move |id_i64: INT| -> Result<Group, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_group.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_event_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Event with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_group_by_id",
move |id_i64: INT| -> Result<Group, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_group
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_event_by_id: {}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Event with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
let db_clone_set_contact = db.clone();
db_module.set_native_fn("save_contact", move |contact: Contact| -> Result<Contact, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_contact.set(&contact)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_contact: {}", e).into(), Position::NONE)))?;
// Return the updated contact with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_contact",
move |contact: Contact| -> Result<Contact, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_contact.set(&contact).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error set_contact: {}", e).into(),
Position::NONE,
))
})?;
// Return the updated contact with the correct ID
Ok(result.1)
},
);
// Manually register database functions as they need to capture 'db'
let db_clone_delete_contact = db.clone();
db_module.set_native_fn("delete_contact", move |contact: Contact| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_contact.collection::<Contact>()
.expect("can open contact collection")
.delete_by_id(contact.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
});
db_module.set_native_fn(
"delete_contact",
move |contact: Contact| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_contact
.collection::<Contact>()
.expect("can open contact collection")
.delete_by_id(contact.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
},
);
let db_clone_get_contact = db.clone();
db_module.set_native_fn("get_contact_by_id", move |id_i64: INT| -> Result<Contact, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_contact.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_contact_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Contact with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_contact_by_id",
move |id_i64: INT| -> Result<Contact, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_contact
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_contact_by_id: {}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Contact with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
// Add list_contacts function to get all contacts
let db_clone_list_contacts = db.clone();
db_module.set_native_fn("list_contacts", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_contacts.collection::<Contact>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get contact collection: {:?}", e).into(),
Position::NONE
)))?;
let contacts = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all contacts: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for contact in contacts {
array.push(Dynamic::from(contact));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_contacts",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_contacts
.collection::<Contact>()
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get contact collection: {:?}", e).into(),
Position::NONE,
))
})?;
let contacts = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all contacts: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for contact in contacts {
array.push(Dynamic::from(contact));
}
Ok(Dynamic::from(array))
},
);
// Add list_events function to get all events
let db_clone_list_groups = db.clone();
db_module.set_native_fn("list_groups", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_groups.collection::<Group>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get group collection: {:?}", e).into(),
Position::NONE
)))?;
let groups = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all groups: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for group in groups {
array.push(Dynamic::from(group));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_groups",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_groups.collection::<Group>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get group collection: {:?}", e).into(),
Position::NONE,
))
})?;
let groups = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all groups: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for group in groups {
array.push(Dynamic::from(group));
}
Ok(Dynamic::from(array))
},
);
// Register the database module globally
engine.register_global_module(db_module.into());

View File

@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
pub struct Comment {
pub base_data: BaseModelData, // Provides id, created_at, updated_at
#[index]
pub user_id: u32, // Maps to commenter_id
pub user_id: u32, // Maps to commenter_id
pub content: String, // Maps to text
pub parent_comment_id: Option<u32>, // For threaded comments
}

View File

@@ -4,4 +4,3 @@ pub mod model;
// Re-export key types for convenience
pub use comment::Comment;

View File

@@ -0,0 +1 @@

View File

@@ -1,9 +1,9 @@
// heromodels/src/models/finance/account.rs
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder};
use heromodels_derive::model;
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
use super::asset::Asset;
@@ -18,7 +18,7 @@ pub struct Account {
pub ledger: String, // describes the ledger/blockchain where the account is located
pub address: String, // address of the account on the blockchain
pub pubkey: String, // public key
pub assets: Vec<u32>, // list of assets in this account
pub assets: Vec<u32>, // list of assets in this account
}
impl Account {
@@ -87,6 +87,6 @@ impl Account {
/// Find an asset by name
pub fn find_asset_by_name(&self, _name: &str) -> Option<&Asset> {
// TODO: implement
return None
return None;
}
}

View File

@@ -1,9 +1,9 @@
// heromodels/src/models/finance/asset.rs
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder};
use heromodels_derive::model;
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
/// AssetType defines the type of blockchain asset
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]

View File

@@ -1,10 +1,10 @@
// heromodels/src/models/finance/marketplace.rs
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder};
use chrono::{DateTime, Utc};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
use super::asset::AssetType;
@@ -53,8 +53,7 @@ impl Default for BidStatus {
}
/// Bid represents a bid on an auction listing
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
#[derive(Default)]
#[derive(Debug, Clone, Serialize, Deserialize, CustomType, Default)]
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
@@ -272,7 +271,7 @@ impl Listing {
}
self.status = ListingStatus::Sold;
if self.sold_at.is_none() {
self.sold_at = Some(Utc::now());
}
@@ -281,7 +280,7 @@ impl Listing {
if self.listing_type == ListingType::Auction {
let buyer_id_str = self.buyer_id.as_ref().unwrap().to_string();
let sale_price_val = self.sale_price.unwrap();
for bid in &mut self.bids {
if bid.bidder_id.to_string() == buyer_id_str && bid.amount == sale_price_val {
bid.status = BidStatus::Accepted;

View File

@@ -9,4 +9,4 @@ 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};
pub use self::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};

View File

@@ -1,12 +1,12 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use chrono::Utc;
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use super::account::Account;
use super::asset::{Asset, AssetType};
use super::marketplace::{Listing, Bid, ListingStatus, ListingType, BidStatus};
use super::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
use crate::db::hero::OurDB;
use crate::db::{Collection, Db};
@@ -18,12 +18,12 @@ type RhaiBid = Bid;
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
u32::try_from(id_i64).map_err(|_|
u32::try_from(id_i64).map_err(|_| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert ID '{}' to u32", id_i64).into(),
Position::NONE
Position::NONE,
))
)
})
}
// Helper functions for enum conversions
@@ -45,8 +45,12 @@ fn string_to_asset_type(s: &str) -> Result<AssetType, Box<EvalAltResult>> {
"Erc721" => Ok(AssetType::Erc721),
"Erc1155" => Ok(AssetType::Erc1155),
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid asset type: '{}'. Expected one of: Native, Erc20, Erc721, Erc1155", s).into(),
Position::NONE
format!(
"Invalid asset type: '{}'. Expected one of: Native, Erc20, Erc721, Erc1155",
s
)
.into(),
Position::NONE,
))),
}
}
@@ -68,8 +72,12 @@ fn string_to_listing_status(s: &str) -> Result<ListingStatus, Box<EvalAltResult>
"Cancelled" => Ok(ListingStatus::Cancelled),
"Expired" => Ok(ListingStatus::Expired),
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid listing status: '{}'. Expected one of: Active, Sold, Cancelled, Expired", s).into(),
Position::NONE
format!(
"Invalid listing status: '{}'. Expected one of: Active, Sold, Cancelled, Expired",
s
)
.into(),
Position::NONE,
))),
}
}
@@ -89,8 +97,12 @@ fn string_to_listing_type(s: &str) -> Result<ListingType, Box<EvalAltResult>> {
"Auction" => Ok(ListingType::Auction),
"Exchange" => Ok(ListingType::Exchange),
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid listing type: '{}'. Expected one of: FixedPrice, Auction, Exchange", s).into(),
Position::NONE
format!(
"Invalid listing type: '{}'. Expected one of: FixedPrice, Auction, Exchange",
s
)
.into(),
Position::NONE,
))),
}
}
@@ -112,8 +124,12 @@ fn string_to_bid_status(s: &str) -> Result<BidStatus, Box<EvalAltResult>> {
"Rejected" => Ok(BidStatus::Rejected),
"Cancelled" => Ok(BidStatus::Cancelled),
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid bid status: '{}'. Expected one of: Active, Accepted, Rejected, Cancelled", s).into(),
Position::NONE
format!(
"Invalid bid status: '{}'. Expected one of: Active, Accepted, Rejected, Cancelled",
s
)
.into(),
Position::NONE,
))),
}
}
@@ -181,7 +197,10 @@ mod account_module {
// Setters using builder pattern with proper mutability handling
#[rhai_fn(return_raw, global)]
pub fn name(account: &mut RhaiAccount, name: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
pub fn name(
account: &mut RhaiAccount,
name: String,
) -> Result<RhaiAccount, Box<EvalAltResult>> {
let mut acc = mem::take(account);
acc = acc.name(name);
*account = acc;
@@ -189,7 +208,10 @@ mod account_module {
}
#[rhai_fn(return_raw, global)]
pub fn user_id(account: &mut RhaiAccount, user_id: INT) -> Result<RhaiAccount, Box<EvalAltResult>> {
pub fn user_id(
account: &mut RhaiAccount,
user_id: INT,
) -> Result<RhaiAccount, Box<EvalAltResult>> {
let user_id = id_from_i64_to_u32(user_id)?;
let mut acc = mem::take(account);
acc = acc.user_id(user_id);
@@ -198,7 +220,10 @@ mod account_module {
}
#[rhai_fn(return_raw, global)]
pub fn description(account: &mut RhaiAccount, description: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
pub fn description(
account: &mut RhaiAccount,
description: String,
) -> Result<RhaiAccount, Box<EvalAltResult>> {
let mut acc = mem::take(account);
acc = acc.description(description);
*account = acc;
@@ -206,7 +231,10 @@ mod account_module {
}
#[rhai_fn(return_raw, global)]
pub fn ledger(account: &mut RhaiAccount, ledger: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
pub fn ledger(
account: &mut RhaiAccount,
ledger: String,
) -> Result<RhaiAccount, Box<EvalAltResult>> {
let mut acc = mem::take(account);
acc = acc.ledger(ledger);
*account = acc;
@@ -214,7 +242,10 @@ mod account_module {
}
#[rhai_fn(return_raw, global)]
pub fn address(account: &mut RhaiAccount, address: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
pub fn address(
account: &mut RhaiAccount,
address: String,
) -> Result<RhaiAccount, Box<EvalAltResult>> {
let mut acc = mem::take(account);
acc = acc.address(address);
*account = acc;
@@ -222,7 +253,10 @@ mod account_module {
}
#[rhai_fn(return_raw, global)]
pub fn pubkey(account: &mut RhaiAccount, pubkey: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
pub fn pubkey(
account: &mut RhaiAccount,
pubkey: String,
) -> Result<RhaiAccount, Box<EvalAltResult>> {
let mut acc = mem::take(account);
acc = acc.pubkey(pubkey);
*account = acc;
@@ -230,7 +264,10 @@ mod account_module {
}
#[rhai_fn(return_raw, global)]
pub fn add_asset(account: &mut RhaiAccount, asset_id: INT) -> Result<RhaiAccount, Box<EvalAltResult>> {
pub fn add_asset(
account: &mut RhaiAccount,
asset_id: INT,
) -> Result<RhaiAccount, Box<EvalAltResult>> {
let asset_id = id_from_i64_to_u32(asset_id)?;
let mut acc = mem::take(account);
acc = acc.add_asset(asset_id);
@@ -301,7 +338,10 @@ mod asset_module {
}
#[rhai_fn(return_raw, global)]
pub fn description(asset: &mut RhaiAsset, description: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
pub fn description(
asset: &mut RhaiAsset,
description: String,
) -> Result<RhaiAsset, Box<EvalAltResult>> {
let mut ast = mem::take(asset);
ast = ast.description(description);
*asset = ast;
@@ -317,7 +357,10 @@ mod asset_module {
}
#[rhai_fn(return_raw, global)]
pub fn address(asset: &mut RhaiAsset, address: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
pub fn address(
asset: &mut RhaiAsset,
address: String,
) -> Result<RhaiAsset, Box<EvalAltResult>> {
let mut ast = mem::take(asset);
ast = ast.address(address);
*asset = ast;
@@ -325,7 +368,10 @@ mod asset_module {
}
#[rhai_fn(return_raw, global)]
pub fn asset_type(asset: &mut RhaiAsset, asset_type_str: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
pub fn asset_type(
asset: &mut RhaiAsset,
asset_type_str: String,
) -> Result<RhaiAsset, Box<EvalAltResult>> {
let asset_type_enum = string_to_asset_type(&asset_type_str)?;
let mut ast = mem::take(asset);
ast = ast.asset_type(asset_type_enum);
@@ -338,7 +384,7 @@ mod asset_module {
if decimals < 0 || decimals > 255 {
return Err(Box::new(EvalAltResult::ErrorArithmetic(
format!("Decimals value must be between 0 and 255, got {}", decimals).into(),
Position::NONE
Position::NONE,
)));
}
let mut ast = mem::take(asset);
@@ -441,7 +487,10 @@ mod listing_module {
// Setters using builder pattern with proper mutability handling
#[rhai_fn(return_raw, global)]
pub fn seller_id(listing: &mut RhaiListing, seller_id: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn seller_id(
listing: &mut RhaiListing,
seller_id: INT,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let seller_id = id_from_i64_to_u32(seller_id)?;
let mut lst = mem::take(listing);
lst = lst.seller_id(seller_id);
@@ -450,7 +499,10 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn asset_id(listing: &mut RhaiListing, asset_id: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn asset_id(
listing: &mut RhaiListing,
asset_id: INT,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let asset_id = id_from_i64_to_u32(asset_id)?;
let mut lst = mem::take(listing);
lst = lst.asset_id(asset_id);
@@ -467,7 +519,10 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn currency(listing: &mut RhaiListing, currency: String) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn currency(
listing: &mut RhaiListing,
currency: String,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let mut lst = mem::take(listing);
lst = lst.currency(currency);
*listing = lst;
@@ -475,7 +530,10 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn listing_type(listing: &mut RhaiListing, listing_type_str: String) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn listing_type(
listing: &mut RhaiListing,
listing_type_str: String,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let listing_type_enum = string_to_listing_type(&listing_type_str)?;
let mut lst = mem::take(listing);
lst = lst.listing_type(listing_type_enum);
@@ -484,7 +542,10 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn title(listing: &mut RhaiListing, title: String) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn title(
listing: &mut RhaiListing,
title: String,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let mut lst = mem::take(listing);
lst = lst.title(title);
*listing = lst;
@@ -492,7 +553,10 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn description(listing: &mut RhaiListing, description: String) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn description(
listing: &mut RhaiListing,
description: String,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let mut lst = mem::take(listing);
lst = lst.description(description);
*listing = lst;
@@ -500,7 +564,10 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn image_url(listing: &mut RhaiListing, image_url: String) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn image_url(
listing: &mut RhaiListing,
image_url: String,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let mut lst = mem::take(listing);
lst = lst.image_url(Some(image_url));
*listing = lst;
@@ -508,15 +575,21 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn expires_at(listing: &mut RhaiListing, end_date_ts: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn expires_at(
listing: &mut RhaiListing,
end_date_ts: INT,
) -> Result<RhaiListing, Box<EvalAltResult>> {
use chrono::TimeZone;
let end_date = chrono::Utc.timestamp_opt(end_date_ts, 0)
let end_date = chrono::Utc
.timestamp_opt(end_date_ts, 0)
.single()
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid timestamp: {}", end_date_ts).into(),
Position::NONE
)))?;
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid timestamp: {}", end_date_ts).into(),
Position::NONE,
))
})?;
let mut lst = mem::take(listing);
lst = lst.expires_at(Some(end_date));
*listing = lst;
@@ -524,7 +597,10 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn add_tag(listing: &mut RhaiListing, tag: String) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn add_tag(
listing: &mut RhaiListing,
tag: String,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let mut lst = mem::take(listing);
lst = lst.add_tag(tag);
*listing = lst;
@@ -532,44 +608,50 @@ mod listing_module {
}
#[rhai_fn(return_raw, global)]
pub fn add_bid(listing: &mut RhaiListing, bid: RhaiBid) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn add_bid(
listing: &mut RhaiListing,
bid: RhaiBid,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let lst = mem::take(listing);
match lst.clone().add_bid(bid) {
Ok(updated_lst) => {
*listing = updated_lst;
Ok(listing.clone())
},
}
Err(err) => {
// Put back the original listing since the add_bid failed
*listing = lst;
Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to add bid: {}", err).into(),
Position::NONE
Position::NONE,
)))
}
}
}
#[rhai_fn(return_raw, global)]
pub fn complete_sale(listing: &mut RhaiListing, buyer_id: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
pub fn complete_sale(
listing: &mut RhaiListing,
buyer_id: INT,
) -> Result<RhaiListing, Box<EvalAltResult>> {
let buyer_id = id_from_i64_to_u32(buyer_id)?;
let lst = mem::take(listing);
// First set the buyer_id and sale_price
let lst_with_buyer = lst.clone().buyer_id(buyer_id);
// Now complete the sale
match lst_with_buyer.complete_sale() {
Ok(updated_lst) => {
*listing = updated_lst;
Ok(listing.clone())
},
}
Err(err) => {
// Put back the original listing since the complete_sale failed
*listing = lst;
Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to complete sale: {}", err).into(),
Position::NONE
Position::NONE,
)))
}
}
@@ -582,13 +664,13 @@ mod listing_module {
Ok(updated_lst) => {
*listing = updated_lst;
Ok(listing.clone())
},
}
Err(err) => {
// Put back the original listing since the cancel failed
*listing = lst;
Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to cancel listing: {}", err).into(),
Position::NONE
Position::NONE,
)))
}
}
@@ -647,7 +729,10 @@ mod bid_module {
// Setters using builder pattern with proper mutability handling
#[rhai_fn(return_raw, global)]
pub fn listing_id(bid: &mut RhaiBid, listing_id: String) -> Result<RhaiBid, Box<EvalAltResult>> {
pub fn listing_id(
bid: &mut RhaiBid,
listing_id: String,
) -> Result<RhaiBid, Box<EvalAltResult>> {
let mut b = mem::take(bid);
b = b.listing_id(listing_id);
*bid = b;
@@ -680,7 +765,10 @@ mod bid_module {
}
#[rhai_fn(return_raw, global)]
pub fn update_status(bid: &mut RhaiBid, status_str: String) -> Result<RhaiBid, Box<EvalAltResult>> {
pub fn update_status(
bid: &mut RhaiBid,
status_str: String,
) -> Result<RhaiBid, Box<EvalAltResult>> {
let status = string_to_bid_status(&status_str)?;
let mut b = mem::take(bid);
b = b.status(status);
@@ -706,13 +794,21 @@ pub fn register_finance_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
engine.register_global_module(bid_module.into());
// --- Global Helper Functions (Enum conversions) ---
engine.register_fn("str_to_asset_type", |s: ImmutableString| self::string_to_asset_type(s.as_str()));
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("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("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("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);
// --- Database interaction functions (registered in a separate db_module) ---
@@ -720,68 +816,158 @@ pub fn register_finance_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Account DB functions
let db_set_account = Arc::clone(&db);
db_module.set_native_fn("set_account", move |account: Account| -> Result<INT, Box<EvalAltResult>> {
let collection = db_set_account.collection::<Account>().map_err(|e|
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Account collection: {:?}", e).into(), Position::NONE)) )?;
collection.set(&account)
.map(|(id, _)| id as INT)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to save Account: {:?}", e).into(), Position::NONE)))
});
db_module.set_native_fn(
"set_account",
move |account: Account| -> Result<INT, Box<EvalAltResult>> {
let collection = db_set_account.collection::<Account>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Account collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection
.set(&account)
.map(|(id, _)| id as INT)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save Account: {:?}", e).into(),
Position::NONE,
))
})
},
);
let db_get_account = Arc::clone(&db);
db_module.set_native_fn("get_account_by_id", move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_rhai)?;
let collection = db_get_account.collection::<Account>().map_err(|e|
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Account collection: {:?}", e).into(), Position::NONE)) )?;
collection.get_by_id(id_u32)
.map(|opt_account| opt_account.map(Dynamic::from).unwrap_or_else(|| Dynamic::UNIT))
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Account with ID {}: {:?}", id_rhai, e).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_account_by_id",
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_rhai)?;
let collection = db_get_account.collection::<Account>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Account collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection
.get_by_id(id_u32)
.map(|opt_account| {
opt_account
.map(Dynamic::from)
.unwrap_or_else(|| Dynamic::UNIT)
})
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Account with ID {}: {:?}", id_rhai, e).into(),
Position::NONE,
))
})
},
);
// Asset DB functions
let db_set_asset = Arc::clone(&db);
db_module.set_native_fn("set_asset", move |asset: Asset| -> Result<INT, Box<EvalAltResult>> {
let collection = db_set_asset.collection::<Asset>().map_err(|e|
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Asset collection: {:?}", e).into(), Position::NONE)) )?;
collection.set(&asset)
.map(|(id, _)| id as INT)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to save Asset: {:?}", e).into(), Position::NONE)))
});
db_module.set_native_fn(
"set_asset",
move |asset: Asset| -> Result<INT, Box<EvalAltResult>> {
let collection = db_set_asset.collection::<Asset>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Asset collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection
.set(&asset)
.map(|(id, _)| id as INT)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save Asset: {:?}", e).into(),
Position::NONE,
))
})
},
);
let db_get_asset = Arc::clone(&db);
db_module.set_native_fn("get_asset_by_id", move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_rhai)?;
let collection = db_get_asset.collection::<Asset>().map_err(|e|
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Asset collection: {:?}", e).into(), Position::NONE)) )?;
collection.get_by_id(id_u32)
.map(|opt_asset| opt_asset.map(Dynamic::from).unwrap_or_else(|| Dynamic::UNIT))
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Asset with ID {}: {:?}", id_rhai, e).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_asset_by_id",
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_rhai)?;
let collection = db_get_asset.collection::<Asset>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Asset collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection
.get_by_id(id_u32)
.map(|opt_asset| {
opt_asset
.map(Dynamic::from)
.unwrap_or_else(|| Dynamic::UNIT)
})
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Asset with ID {}: {:?}", id_rhai, e).into(),
Position::NONE,
))
})
},
);
// Listing DB functions
let db_set_listing = Arc::clone(&db);
db_module.set_native_fn("set_listing", move |listing: Listing| -> Result<INT, Box<EvalAltResult>> {
let collection = db_set_listing.collection::<Listing>().map_err(|e|
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Listing collection: {:?}", e).into(), Position::NONE)) )?;
collection.set(&listing)
.map(|(id, _)| id as INT)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to save Listing: {:?}", e).into(), Position::NONE)))
});
db_module.set_native_fn(
"set_listing",
move |listing: Listing| -> Result<INT, Box<EvalAltResult>> {
let collection = db_set_listing.collection::<Listing>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Listing collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection
.set(&listing)
.map(|(id, _)| id as INT)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to save Listing: {:?}", e).into(),
Position::NONE,
))
})
},
);
let db_get_listing = Arc::clone(&db);
db_module.set_native_fn("get_listing_by_id", move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_rhai)?;
let collection = db_get_listing.collection::<Listing>().map_err(|e|
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Listing collection: {:?}", e).into(), Position::NONE)) )?;
collection.get_by_id(id_u32)
.map(|opt_listing| opt_listing.map(Dynamic::from).unwrap_or_else(|| Dynamic::UNIT))
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Listing with ID {}: {:?}", id_rhai, e).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_listing_by_id",
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_rhai)?;
let collection = db_get_listing.collection::<Listing>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Listing collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection
.get_by_id(id_u32)
.map(|opt_listing| {
opt_listing
.map(Dynamic::from)
.unwrap_or_else(|| Dynamic::UNIT)
})
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get Listing with ID {}: {:?}", id_rhai, e).into(),
Position::NONE,
))
})
},
);
engine.register_global_module(db_module.into());
// Global timestamp function for scripts to get current time
engine.register_fn("timestamp", || Utc::now().timestamp());
println!("Successfully registered finance Rhai module.");
}

View File

@@ -1,7 +1,7 @@
use super::flow_step::FlowStep;
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use super::flow_step::FlowStep;
/// Represents a signing flow.
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default)]
@@ -33,7 +33,7 @@ impl Flow {
Self {
base_data: BaseModelData::new(),
flow_uuid: flow_uuid.to_string(),
name: String::new(), // Default name, to be set by builder
name: String::new(), // Default name, to be set by builder
status: String::from("Pending"), // Default status, to be set by builder
steps: Vec::new(),
}

View File

@@ -1,11 +1,11 @@
// Export flow model submodules
pub mod flow;
pub mod flow_step;
pub mod signature_requirement;
pub mod rhai;
pub mod signature_requirement;
// 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;
pub use signature_requirement::SignatureRequirement;

View File

@@ -1,7 +1,7 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use super::flow::Flow;
use super::flow_step::FlowStep;
@@ -9,18 +9,18 @@ use super::signature_requirement::SignatureRequirement;
type RhaiFlow = Flow;
type RhaiFlowStep = FlowStep;
type RhaiSignatureRequirement = SignatureRequirement;
use crate::db::hero::OurDB;
use crate::db::Collection;
use crate::db::Db;
use crate::db::hero::OurDB;
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
u32::try_from(id_i64).map_err(|_|
u32::try_from(id_i64).map_err(|_| {
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert ID '{}' to u32", id_i64).into(),
Position::NONE
Position::NONE,
))
)
})
}
#[export_module]
@@ -30,7 +30,7 @@ mod rhai_flow_module {
pub fn new_flow(flow_uuid: String) -> RhaiFlow {
Flow::new(flow_uuid)
}
/// Sets the flow name
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn flow_name(flow: &mut RhaiFlow, name: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
@@ -38,41 +38,63 @@ mod rhai_flow_module {
*flow = owned_flow.name(name);
Ok(flow.clone())
}
/// Sets the flow status
#[rhai_fn(name = "status", return_raw, global, pure)]
pub fn flow_status(flow: &mut RhaiFlow, status: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
pub fn flow_status(
flow: &mut RhaiFlow,
status: String,
) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
*flow = owned_flow.status(status);
Ok(flow.clone())
}
/// Adds a step to the flow
#[rhai_fn(name = "add_step", return_raw, global, pure)]
pub fn flow_add_step(flow: &mut RhaiFlow, step: RhaiFlowStep) -> Result<RhaiFlow, Box<EvalAltResult>> {
pub fn flow_add_step(
flow: &mut RhaiFlow,
step: RhaiFlowStep,
) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
*flow = owned_flow.add_step(step);
Ok(flow.clone())
}
// Flow Getters
#[rhai_fn(get = "id", pure)]
pub fn get_id(flow: &mut RhaiFlow) -> i64 { flow.base_data.id as i64 }
pub fn get_id(flow: &mut RhaiFlow) -> i64 {
flow.base_data.id as i64
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_created_at(flow: &mut RhaiFlow) -> i64 { flow.base_data.created_at }
pub fn get_created_at(flow: &mut RhaiFlow) -> i64 {
flow.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_modified_at(flow: &mut RhaiFlow) -> i64 { flow.base_data.modified_at }
pub fn get_modified_at(flow: &mut RhaiFlow) -> i64 {
flow.base_data.modified_at
}
#[rhai_fn(get = "flow_uuid", pure)]
pub fn get_flow_uuid(flow: &mut RhaiFlow) -> String { flow.flow_uuid.clone() }
pub fn get_flow_uuid(flow: &mut RhaiFlow) -> String {
flow.flow_uuid.clone()
}
#[rhai_fn(get = "name", pure)]
pub fn get_name(flow: &mut RhaiFlow) -> String { flow.name.clone() }
pub fn get_name(flow: &mut RhaiFlow) -> String {
flow.name.clone()
}
#[rhai_fn(get = "status", pure)]
pub fn get_status(flow: &mut RhaiFlow) -> String { flow.status.clone() }
pub fn get_status(flow: &mut RhaiFlow) -> String {
flow.status.clone()
}
#[rhai_fn(get = "steps", pure)]
pub fn get_steps(flow: &mut RhaiFlow) -> Array {
flow.steps.iter().cloned().map(Dynamic::from).collect::<Array>()
flow.steps
.iter()
.cloned()
.map(Dynamic::from)
.collect::<Array>()
}
// --- FlowStep Functions ---
#[rhai_fn(global)]
pub fn new_flow_step(step_order_i64: i64) -> Dynamic {
@@ -81,34 +103,46 @@ mod rhai_flow_module {
let mut flow_step = FlowStep::default();
flow_step.step_order = step_order;
Dynamic::from(flow_step)
},
Err(err) => Dynamic::from(err.to_string())
}
Err(err) => Dynamic::from(err.to_string()),
}
}
/// Sets the flow step description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn flow_step_description(step: &mut RhaiFlowStep, description: String) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
pub fn flow_step_description(
step: &mut RhaiFlowStep,
description: String,
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
let owned_step = mem::replace(step, FlowStep::default()); // Use Default trait
*step = owned_step.description(description);
Ok(step.clone())
}
/// Sets the flow step status
#[rhai_fn(name = "status", return_raw, global, pure)]
pub fn flow_step_status(step: &mut RhaiFlowStep, status: String) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
pub fn flow_step_status(
step: &mut RhaiFlowStep,
status: String,
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
let owned_step = mem::replace(step, FlowStep::default()); // Use Default trait
*step = owned_step.status(status);
Ok(step.clone())
}
// FlowStep Getters
#[rhai_fn(get = "id", pure)]
pub fn get_step_id(step: &mut RhaiFlowStep) -> i64 { step.base_data.id as i64 }
pub fn get_step_id(step: &mut RhaiFlowStep) -> i64 {
step.base_data.id as i64
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_step_created_at(step: &mut RhaiFlowStep) -> i64 { step.base_data.created_at }
pub fn get_step_created_at(step: &mut RhaiFlowStep) -> i64 {
step.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_step_modified_at(step: &mut RhaiFlowStep) -> i64 { step.base_data.modified_at }
pub fn get_step_modified_at(step: &mut RhaiFlowStep) -> i64 {
step.base_data.modified_at
}
#[rhai_fn(get = "description", pure)]
pub fn get_step_description(step: &mut RhaiFlowStep) -> Dynamic {
match &step.description {
@@ -117,14 +151,22 @@ mod rhai_flow_module {
}
}
#[rhai_fn(get = "step_order", pure)]
pub fn get_step_order(step: &mut RhaiFlowStep) -> i64 { step.step_order as i64 }
pub fn get_step_order(step: &mut RhaiFlowStep) -> i64 {
step.step_order as i64
}
#[rhai_fn(get = "status", pure)]
pub fn get_step_status(step: &mut RhaiFlowStep) -> String { step.status.clone() }
pub fn get_step_status(step: &mut RhaiFlowStep) -> String {
step.status.clone()
}
// --- SignatureRequirement Functions ---
/// Create a new signature requirement
#[rhai_fn(global)]
pub fn new_signature_requirement(flow_step_id_i64: i64, public_key: String, message: String) -> Dynamic {
pub fn new_signature_requirement(
flow_step_id_i64: i64,
public_key: String,
message: String,
) -> Dynamic {
match id_from_i64_to_u32(flow_step_id_i64) {
Ok(flow_step_id) => {
let mut signature_requirement = SignatureRequirement::default();
@@ -132,48 +174,69 @@ mod rhai_flow_module {
signature_requirement.public_key = public_key;
signature_requirement.message = message;
Dynamic::from(signature_requirement)
},
Err(err) => Dynamic::from(err.to_string())
}
Err(err) => Dynamic::from(err.to_string()),
}
}
/// Sets the signed_by field
#[rhai_fn(name = "signed_by", return_raw, global, pure)]
pub fn signature_requirement_signed_by(sr: &mut RhaiSignatureRequirement, signed_by: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
pub fn signature_requirement_signed_by(
sr: &mut RhaiSignatureRequirement,
signed_by: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
*sr = owned_sr.signed_by(signed_by);
Ok(sr.clone())
}
/// Sets the signature field
#[rhai_fn(name = "signature", return_raw, global, pure)]
pub fn signature_requirement_signature(sr: &mut RhaiSignatureRequirement, signature: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
pub fn signature_requirement_signature(
sr: &mut RhaiSignatureRequirement,
signature: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
*sr = owned_sr.signature(signature);
Ok(sr.clone())
}
/// Sets the status field
#[rhai_fn(name = "status", return_raw, global, pure)]
pub fn signature_requirement_status(sr: &mut RhaiSignatureRequirement, status: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
pub fn signature_requirement_status(
sr: &mut RhaiSignatureRequirement,
status: String,
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
*sr = owned_sr.status(status);
Ok(sr.clone())
}
// SignatureRequirement Getters
#[rhai_fn(get = "id", pure)]
pub fn get_sr_id(sr: &mut RhaiSignatureRequirement) -> i64 { sr.base_data.id as i64 }
pub fn get_sr_id(sr: &mut RhaiSignatureRequirement) -> i64 {
sr.base_data.id as i64
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_sr_created_at(sr: &mut RhaiSignatureRequirement) -> i64 { sr.base_data.created_at }
pub fn get_sr_created_at(sr: &mut RhaiSignatureRequirement) -> i64 {
sr.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_sr_modified_at(sr: &mut RhaiSignatureRequirement) -> i64 { sr.base_data.modified_at }
pub fn get_sr_modified_at(sr: &mut RhaiSignatureRequirement) -> i64 {
sr.base_data.modified_at
}
#[rhai_fn(get = "flow_step_id", pure)]
pub fn get_sr_flow_step_id(sr: &mut RhaiSignatureRequirement) -> i64 { sr.flow_step_id as i64 }
pub fn get_sr_flow_step_id(sr: &mut RhaiSignatureRequirement) -> i64 {
sr.flow_step_id as i64
}
#[rhai_fn(get = "public_key", pure)]
pub fn get_sr_public_key(sr: &mut RhaiSignatureRequirement) -> String { sr.public_key.clone() }
pub fn get_sr_public_key(sr: &mut RhaiSignatureRequirement) -> String {
sr.public_key.clone()
}
#[rhai_fn(get = "message", pure)]
pub fn get_sr_message(sr: &mut RhaiSignatureRequirement) -> String { sr.message.clone() }
pub fn get_sr_message(sr: &mut RhaiSignatureRequirement) -> String {
sr.message.clone()
}
#[rhai_fn(get = "signed_by", pure)]
pub fn get_sr_signed_by(sr: &mut RhaiSignatureRequirement) -> Dynamic {
match &sr.signed_by {
@@ -189,185 +252,284 @@ mod rhai_flow_module {
}
}
#[rhai_fn(get = "status", pure)]
pub fn get_sr_status(sr: &mut RhaiSignatureRequirement) -> String { sr.status.clone() }
pub fn get_sr_status(sr: &mut RhaiSignatureRequirement) -> String {
sr.status.clone()
}
}
/// Register the flow module with the Rhai engine
pub fn register_flow_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Create a module for database functions
let mut db_module = Module::new();
// Flow database functions
let db_clone = Arc::clone(&db);
db_module.set_native_fn("save_flow", move |flow: Flow| -> Result<Flow, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone.set(&flow)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error save_flow: {:?}", e).into(), Position::NONE)))?;
// Return the updated flow with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_flow",
move |flow: Flow| -> Result<Flow, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone.set(&flow).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error save_flow: {:?}", e).into(),
Position::NONE,
))
})?;
// Return the updated flow with the correct ID
Ok(result.1)
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("get_flow_by_id", move |id_i64: INT| -> Result<Flow, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_flow_by_id: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Flow with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_flow_by_id",
move |id_i64: INT| -> Result<Flow, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_flow_by_id: {:?}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Flow with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("delete_flow", move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
let collection = db_clone.collection::<Flow>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow collection: {:?}", e).into(),
Position::NONE
)))?;
collection.delete_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to delete Flow (ID: {}): {:?}", id_u32, e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"delete_flow",
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
let collection = db_clone.collection::<Flow>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection.delete_by_id(id_u32).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to delete Flow (ID: {}): {:?}", id_u32, e).into(),
Position::NONE,
))
})
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("list_flows", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone.collection::<Flow>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow collection: {:?}", e).into(),
Position::NONE
)))?;
let flows = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all flows: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for flow in flows {
array.push(Dynamic::from(flow));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_flows",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone.collection::<Flow>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow collection: {:?}", e).into(),
Position::NONE,
))
})?;
let flows = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all flows: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for flow in flows {
array.push(Dynamic::from(flow));
}
Ok(Dynamic::from(array))
},
);
// FlowStep database functions
let db_clone = Arc::clone(&db);
db_module.set_native_fn("save_flow_step", move |step: FlowStep| -> Result<FlowStep, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone.set(&step)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error save_flow_step: {:?}", e).into(), Position::NONE)))?;
// Return the updated flow step with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_flow_step",
move |step: FlowStep| -> Result<FlowStep, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone.set(&step).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error save_flow_step: {:?}", e).into(),
Position::NONE,
))
})?;
// Return the updated flow step with the correct ID
Ok(result.1)
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("get_flow_step_by_id", move |id_i64: INT| -> Result<FlowStep, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_flow_step_by_id: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("FlowStep with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_flow_step_by_id",
move |id_i64: INT| -> Result<FlowStep, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_flow_step_by_id: {:?}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("FlowStep with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("delete_flow_step", move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
let collection = db_clone.collection::<FlowStep>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow step collection: {:?}", e).into(),
Position::NONE
)))?;
collection.delete_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to delete FlowStep (ID: {}): {:?}", id_u32, e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"delete_flow_step",
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
let collection = db_clone.collection::<FlowStep>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow step collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection.delete_by_id(id_u32).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to delete FlowStep (ID: {}): {:?}", id_u32, e).into(),
Position::NONE,
))
})
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("list_flow_steps", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone.collection::<FlowStep>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow step collection: {:?}", e).into(),
Position::NONE
)))?;
let steps = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all flow steps: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for step in steps {
array.push(Dynamic::from(step));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_flow_steps",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone.collection::<FlowStep>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get flow step collection: {:?}", e).into(),
Position::NONE,
))
})?;
let steps = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all flow steps: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for step in steps {
array.push(Dynamic::from(step));
}
Ok(Dynamic::from(array))
},
);
// SignatureRequirement database functions
let db_clone = Arc::clone(&db);
db_module.set_native_fn("save_signature_requirement", move |sr: SignatureRequirement| -> Result<SignatureRequirement, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone.set(&sr)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error save_signature_requirement: {:?}", e).into(), Position::NONE)))?;
// Return the updated signature requirement with the correct ID
Ok(result.1)
});
db_module.set_native_fn(
"save_signature_requirement",
move |sr: SignatureRequirement| -> Result<SignatureRequirement, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone.set(&sr).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error save_signature_requirement: {:?}", e).into(),
Position::NONE,
))
})?;
// Return the updated signature requirement with the correct ID
Ok(result.1)
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("get_signature_requirement_by_id", move |id_i64: INT| -> Result<SignatureRequirement, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_signature_requirement_by_id: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("SignatureRequirement with ID {} not found", id_u32).into(), Position::NONE)))
});
db_module.set_native_fn(
"get_signature_requirement_by_id",
move |id_i64: INT| -> Result<SignatureRequirement, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone
.get_by_id(id_u32)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error get_signature_requirement_by_id: {:?}", e).into(),
Position::NONE,
))
})?
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorRuntime(
format!("SignatureRequirement with ID {} not found", id_u32).into(),
Position::NONE,
))
})
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("delete_signature_requirement", move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
let collection = db_clone.collection::<SignatureRequirement>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get signature requirement collection: {:?}", e).into(),
Position::NONE
)))?;
collection.delete_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to delete SignatureRequirement (ID: {}): {:?}", id_u32, e).into(),
Position::NONE
)))
});
db_module.set_native_fn(
"delete_signature_requirement",
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
let collection = db_clone.collection::<SignatureRequirement>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get signature requirement collection: {:?}", e).into(),
Position::NONE,
))
})?;
collection.delete_by_id(id_u32).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!(
"Failed to delete SignatureRequirement (ID: {}): {:?}",
id_u32, e
)
.into(),
Position::NONE,
))
})
},
);
let db_clone = Arc::clone(&db);
db_module.set_native_fn("list_signature_requirements", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone.collection::<SignatureRequirement>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get signature requirement collection: {:?}", e).into(),
Position::NONE
)))?;
let srs = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all signature requirements: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for sr in srs {
array.push(Dynamic::from(sr));
}
Ok(Dynamic::from(array))
});
db_module.set_native_fn(
"list_signature_requirements",
move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone.collection::<SignatureRequirement>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get signature requirement collection: {:?}", e).into(),
Position::NONE,
))
})?;
let srs = collection.get_all().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all signature requirements: {:?}", e).into(),
Position::NONE,
))
})?;
let mut array = Array::new();
for sr in srs {
array.push(Dynamic::from(sr));
}
Ok(Dynamic::from(array))
},
);
// Register the database module globally
engine.register_static_module("db", db_module.into());
// Register the flow module using exported_module! macro
let module = exported_module!(rhai_flow_module);
engine.register_global_module(module.into());
println!("Flow Rhai module registered.");
}

View File

@@ -32,7 +32,12 @@ 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) -> Self {
pub fn new(
_id: u32,
flow_step_id: u32,
public_key: impl ToString,
message: impl ToString,
) -> Self {
Self {
base_data: BaseModelData::new(),
flow_step_id,

View File

@@ -4,5 +4,5 @@ pub mod proposal;
pub mod attached_file;
pub use self::proposal::{Proposal, Ballot, VoteOption, ProposalStatus, VoteEventStatus};
pub use self::attached_file::AttachedFile;
pub use self::attached_file::AttachedFile;
pub use self::proposal::{Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption};

View File

@@ -5,9 +5,9 @@ use heromodels_derive::model; // For #[model]
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
use heromodels_core::BaseModelData;
use crate::models::core::Comment;
use super::AttachedFile;
use crate::models::core::Comment;
use heromodels_core::BaseModelData;
// --- Enums ---
@@ -72,9 +72,9 @@ impl VoteOption {
#[model] // Has base.Base in V spec
pub struct Ballot {
pub base_data: BaseModelData,
pub user_id: u32, // The ID of the user who cast this ballot
pub vote_option_id: u8, // The 'id' of the VoteOption chosen
pub shares_count: i64, // Number of shares/tokens/voting power
pub user_id: u32, // The ID of the user who cast this ballot
pub vote_option_id: u8, // The 'id' of the VoteOption chosen
pub shares_count: i64, // Number of shares/tokens/voting power
pub comment: Option<String>, // Optional comment from the voter
}
@@ -281,7 +281,7 @@ impl Proposal {
eprintln!("Voting is not open for proposal '{}'", self.title);
return self;
}
// Check if the option exists
if !self.options.iter().any(|opt| opt.id == chosen_option_id) {
eprintln!(
@@ -290,7 +290,7 @@ impl Proposal {
);
return self;
}
// Check eligibility for private proposals
if let Some(group) = &self.private_group {
if !group.contains(&user_id) {
@@ -301,14 +301,14 @@ impl Proposal {
return self;
}
}
// Create a new ballot with the comment
let mut new_ballot = Ballot::new(ballot_id, user_id, chosen_option_id, shares);
new_ballot.comment = Some(comment.to_string());
// Add the ballot to the proposal
self.ballots.push(new_ballot);
// Update the vote count for the chosen option
if let Some(option) = self
.options
@@ -317,7 +317,7 @@ impl Proposal {
{
option.count += shares;
}
self
}
}

View File

@@ -1,7 +1,7 @@
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use std::fmt;
use serde::{Deserialize, Serialize};
use std::fmt;
// --- Enums ---
@@ -109,7 +109,7 @@ impl ContractSigner {
self.signed_at = Some(signed_at);
self
}
pub fn clear_signed_at(mut self) -> Self {
self.signed_at = None;
self
@@ -139,21 +139,21 @@ pub struct Contract {
pub title: String,
pub description: String,
#[index]
pub contract_type: String,
#[index]
pub status: crate::models::ContractStatus, // Use re-exported path for #[model] macro
pub created_by: String,
pub terms_and_conditions: String,
pub start_date: Option<u64>,
pub end_date: Option<u64>,
pub renewal_period_days: Option<i32>,
pub next_renewal_date: Option<u64>,
pub signers: Vec<ContractSigner>,
pub revisions: Vec<ContractRevision>,
pub current_version: u32,
@@ -217,7 +217,7 @@ impl Contract {
self.start_date = Some(start_date);
self
}
pub fn clear_start_date(mut self) -> Self {
self.start_date = None;
self
@@ -257,7 +257,7 @@ impl Contract {
self.signers.push(signer);
self
}
pub fn signers(mut self, signers: Vec<ContractSigner>) -> Self {
self.signers = signers;
self
@@ -272,7 +272,7 @@ impl Contract {
self.revisions = revisions;
self
}
pub fn current_version(mut self, version: u32) -> Self {
self.current_version = version;
self
@@ -287,7 +287,7 @@ impl Contract {
self.last_signed_date = None;
self
}
// Example methods for state changes
pub fn set_status(&mut self, status: crate::models::ContractStatus) {
self.status = status;

View File

@@ -1,51 +1,60 @@
use rhai::{
Dynamic, Engine, EvalAltResult, NativeCallContext, Position, Module, Array,
};
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module, NativeCallContext, Position};
use std::sync::Arc;
use crate::db::hero::OurDB; // Updated path based on compiler suggestion
// use heromodels_core::BaseModelData; // Removed as fields are accessed via contract.base_data directly
use crate::models::legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
use crate::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>> {
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
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>> {
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
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>> {
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
field_name, object_name, val
),
context_pos,
))
@@ -73,193 +82,532 @@ pub fn register_legal_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
engine.register_static_module("SignerStatusConstants", signer_status_module.into());
engine.register_type_with_name::<SignerStatus>("SignerStatus"); // Expose the type itself
// --- ContractRevision ---
// --- 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))
}
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.call_position(),
"version",
"new_contract_revision",
)?;
let created_at = i64_to_u64(
created_at_i64,
context.call_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))
},
);
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 ---
// --- 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.call_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_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)))
});
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 ---
// --- 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")?;
move |context: NativeCallContext,
base_id_i64: i64,
contract_id: String|
-> Result<Contract, Box<EvalAltResult>> {
let base_id = i64_to_u32(
base_id_i64,
context.call_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("title", |contract: Contract, title: String| -> Contract {
contract.title(title)
});
engine.register_fn("clear_start_date", |contract: Contract| -> Contract { contract.clear_start_date() });
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("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(
"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.call_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("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(
"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.call_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("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(
"renewal_period_days",
|context: NativeCallContext,
contract: Contract,
days_i64: i64|
-> Result<Contract, Box<EvalAltResult>> {
let days_i32 = i64_to_i32(
days_i64,
context.call_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("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(
"next_renewal_date",
|context: NativeCallContext,
contract: Contract,
date_i64: i64|
-> Result<Contract, Box<EvalAltResult>> {
let date_u64 = i64_to_u64(
date_i64,
context.call_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.call_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.call_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()
});
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(
"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("current_version", |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> { Ok(contract.current_version as i64) });
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("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)
});
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);
});
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",
engine.register_fn(
"set_contract",
move |contract: Contract| -> Result<(), Box<EvalAltResult>> {
captured_db_for_set.set(&contract).map(|_| ()).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to set Contract (ID: {}): {}", contract.base_data.id, e).into(),
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",
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,
)))
});
let id_u32 = i64_to_u32(id_i64, context.call_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,
))
})
},
);
}

View File

@@ -1,7 +1,7 @@
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
/// Represents a collection of library items.
#[model]

File diff suppressed because it is too large Load Diff

View File

@@ -3,41 +3,41 @@ pub mod core;
pub mod userexample;
// pub mod productexample; // Temporarily remove as files are missing
pub mod access;
pub mod calendar;
pub mod contact;
pub mod circle;
pub mod governance;
pub mod finance;
pub mod library;
pub mod legal;
pub mod flow;
pub mod biz;
pub mod calendar;
pub mod circle;
pub mod contact;
pub mod finance;
pub mod flow;
pub mod governance;
pub mod legal;
pub mod library;
pub mod projects;
// Re-export key types for convenience
pub use core::Comment;
pub use userexample::User;
// pub use productexample::Product; // Temporarily remove
pub use calendar::{Calendar, Event, Attendee, AttendanceStatus};
pub use circle::{Circle};
pub use governance::{Proposal, ProposalStatus, VoteEventStatus, Ballot, VoteOption, AttachedFile};
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 library::items::{Image, Pdf, Markdown};
pub use calendar::{AttendanceStatus, Attendee, Calendar, Event};
pub use circle::Circle;
pub use finance::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
pub use finance::{Account, Asset, AssetType};
pub use flow::{Flow, FlowStep, SignatureRequirement};
pub use governance::{AttachedFile, Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption};
pub use legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
pub use library::collection::Collection;
pub use library::items::{Image, Markdown, Pdf};
pub use flow::register_flow_rhai_module;
#[cfg(feature = "rhai")]
pub use biz::register_biz_rhai_module;
#[cfg(feature = "rhai")]
pub use calendar::register_calendar_rhai_module;
#[cfg(feature = "rhai")]
pub use circle::register_circle_rhai_module;
pub use flow::register_flow_rhai_module;
pub use legal::register_legal_rhai_module;
#[cfg(feature = "rhai")]
pub use biz::register_biz_rhai_module;
pub use library::register_library_rhai_module;
#[cfg(feature = "rhai")]
pub use projects::register_projects_rhai_module;
#[cfg(feature = "rhai")]
pub use library::register_library_rhai_module;

View File

@@ -1,8 +1,8 @@
// heromodels/src/models/projects/base.rs
use serde::{Deserialize, Serialize};
use heromodels_core::{BaseModelData, Model, BaseModelDataOps};
use heromodels_core::{BaseModelData, BaseModelDataOps, Model};
#[cfg(feature = "rhai")]
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
use strum_macros::Display; // Made unconditional as Display derive is used on non-rhai-gated enums
// --- Enums ---
@@ -50,7 +50,7 @@ pub enum ItemType {
impl Default for ItemType {
fn default() -> Self {
ItemType::Task
}
}
}
// --- Structs ---
@@ -178,7 +178,6 @@ impl Project {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "rhai", derive(CustomType))]
pub struct Label {
@@ -226,4 +225,3 @@ impl Label {
self
}
}

View File

@@ -3,8 +3,8 @@
use chrono::{DateTime, Utc};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
use super::base::Status as ProjectStatus; // Using the generic project status for now
@@ -15,17 +15,17 @@ pub struct Epic {
pub name: String,
pub description: Option<String>,
pub status: ProjectStatus, // Or a new EpicStatus enum if needed
pub project_id: Option<u32>, // Link to a general project/board
pub start_date: Option<DateTime<Utc>>,
pub due_date: Option<DateTime<Utc>>,
pub tags: Vec<String>,
// Explicitly list task IDs belonging to this epic
// This helps in querying and avoids relying solely on tasks pointing to the epic.
pub child_task_ids: Vec<u32>,
pub child_task_ids: Vec<u32>,
}
impl Epic {

View File

@@ -1,11 +1,11 @@
// heromodels/src/models/projects/mod.rs
pub mod base;
pub mod task_enums;
pub mod task;
pub mod epic;
pub mod sprint_enums;
pub mod sprint;
pub mod sprint_enums;
pub mod task;
pub mod task_enums;
// pub mod epic;
// pub mod issue;
// pub mod kanban;
@@ -13,11 +13,11 @@ pub mod sprint;
// pub mod story;
pub use base::*;
pub use task_enums::*;
pub use task::*;
pub use epic::*;
pub use sprint_enums::*;
pub use sprint::*;
pub use sprint_enums::*;
pub use task::*;
pub use task_enums::*;
// pub use epic::*;
// pub use issue::*;
// pub use kanban::*;
@@ -25,7 +25,7 @@ pub use sprint::*;
// pub use story::*;
#[cfg(feature = "rhai")]
pub mod rhai;
pub mod rhai;
#[cfg(feature = "rhai")]
pub use rhai::register_projects_rhai_module;

View File

@@ -1,14 +1,13 @@
// heromodels/src/models/projects/rhai.rs
use rhai::{Engine, EvalAltResult, Dynamic, Position};
use std::sync::Arc;
use crate::db::hero::OurDB;
use heromodels_core::{Model, BaseModelDataOps};
use crate::db::{Db, Collection};
use crate::db::{Collection, Db};
use heromodels_core::{BaseModelDataOps, Model};
use rhai::{Dynamic, Engine, EvalAltResult, Position};
use std::sync::Arc;
// Import models from the projects::base module
use super::base::{Project, /* Label, */ Priority, Status, ItemType}; // Label commented out as it's unused for now
use super::base::{ItemType, /* Label, */ Priority, Project, Status}; // 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>> {
@@ -78,172 +77,325 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
});
// 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)?))
});
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("id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> {
Ok(p.get_id() as i64)
});
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()) });
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,
))
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<String>, Box<EvalAltResult>>>()?;
Ok(p.tags(tags_vec))
});
.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)) });
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("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(
"add_base_comment",
|p: Project, comment_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
Ok(p.add_base_comment(id_from_i64(comment_id_i64)?))
},
);
// --- 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(),
))
})?;
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(|_| ()).map_err(|e| {
Box::new(EvalAltResult::ErrorSystem(
"Failed to save project".to_string(),
format!("DB operation failed: {:?}", e).into(),
))
})
});
collection.set(&project).map(|_| ()).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(),
))
})?;
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(),
))),
}
});
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

View File

@@ -3,8 +3,8 @@
use chrono::{DateTime, Utc};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
use super::sprint_enums::SprintStatus; // Import our new enum
@@ -16,14 +16,14 @@ pub struct Sprint {
pub description: Option<String>,
pub status: SprintStatus,
pub goal: Option<String>, // Sprint goal
pub project_id: Option<u32>, // Link to a general project/board
pub start_date: Option<DateTime<Utc>>,
pub end_date: Option<DateTime<Utc>>, // Changed from due_date for sprints
// Explicitly list task IDs belonging to this sprint
pub task_ids: Vec<u32>,
pub task_ids: Vec<u32>,
}
impl Sprint {

View File

@@ -3,10 +3,10 @@
use chrono::{DateTime, Utc};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder}; // Assuming rhai might be used
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize}; // Assuming rhai might be used
use super::task_enums::{TaskStatus, TaskPriority}; // Import our new enums
use super::task_enums::{TaskPriority, TaskStatus}; // Import our new enums
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
#[model] // This will provide id, created_at, updated_at via base_data
@@ -16,10 +16,10 @@ pub struct Task {
pub description: Option<String>,
pub status: TaskStatus,
pub priority: TaskPriority,
pub assignee_id: Option<u32>, // User ID
pub reporter_id: Option<u32>, // User ID
pub parent_task_id: Option<u32>, // For subtasks
pub epic_id: Option<u32>,
pub sprint_id: Option<u32>,

View File

@@ -2,4 +2,4 @@
pub mod user;
// Re-export User for convenience
pub use user::User;
pub use user::User;