remove rhai dsls
This commit is contained in:
		@@ -1,5 +1,5 @@
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use crate::models::Circle;
 | 
			
		||||
use crate::db::{hero::OurDB, Collection, Db};
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
@@ -90,6 +90,17 @@ pub fn can_access_resource(
 | 
			
		||||
    object_id: u32,
 | 
			
		||||
    _object_type: &str,
 | 
			
		||||
) -> bool {
 | 
			
		||||
    let circle = db
 | 
			
		||||
        .collection::<Circle>()
 | 
			
		||||
        .expect("Failed to get Circle collection")
 | 
			
		||||
        .get_all()
 | 
			
		||||
        .unwrap()[0].clone();
 | 
			
		||||
 | 
			
		||||
    // Circle members can access everything
 | 
			
		||||
    if circle.members.contains(&public_key.to_string()) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("Checking access for public key: {}", public_key);
 | 
			
		||||
 | 
			
		||||
    // get all access records for object
 | 
			
		||||
@@ -112,3 +123,21 @@ pub fn can_access_resource(
 | 
			
		||||
    // if circle_pk is in access records true
 | 
			
		||||
    return access_records.iter().any(|record| record.circle_pk == public_key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn is_circle_member(
 | 
			
		||||
    db: Arc<OurDB>,
 | 
			
		||||
    public_key: &str,
 | 
			
		||||
) -> bool {
 | 
			
		||||
    let circle = db
 | 
			
		||||
        .collection::<Circle>()
 | 
			
		||||
        .expect("Failed to get Circle collection")
 | 
			
		||||
        .get_all()
 | 
			
		||||
        .unwrap()[0].clone();
 | 
			
		||||
 | 
			
		||||
    // Circle members can access everything
 | 
			
		||||
    if circle.members.contains(&public_key.to_string()) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
// Export contact module
 | 
			
		||||
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 rhai::register_access_rhai_module;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,238 +0,0 @@
 | 
			
		||||
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;
 | 
			
		||||
type RhaiAccess = Access;
 | 
			
		||||
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(|_| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!("Failed to convert ID '{}' to u32", id_i64).into(),
 | 
			
		||||
            Position::NONE,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod rhai_access_module {
 | 
			
		||||
    // --- Access Functions ---
 | 
			
		||||
    #[rhai_fn(name = "new_access", return_raw)]
 | 
			
		||||
    pub fn new_access() -> Result<RhaiAccess, Box<EvalAltResult>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        // 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);
 | 
			
		||||
        Ok(access.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(name = "circle_pk", return_raw, global, pure)]
 | 
			
		||||
    pub fn access_circle_pk(
 | 
			
		||||
        access: &mut RhaiAccess,
 | 
			
		||||
        circle_pk: String,
 | 
			
		||||
    ) -> 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_pk(circle_pk);
 | 
			
		||||
        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>> {
 | 
			
		||||
        // 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);
 | 
			
		||||
        Ok(access.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(name = "contact_id", return_raw, global, pure)]
 | 
			
		||||
    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);
 | 
			
		||||
        Ok(access.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[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>> {
 | 
			
		||||
        // 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);
 | 
			
		||||
        Ok(access.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Access Getters
 | 
			
		||||
    #[rhai_fn(get = "id", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "circle_pk", pure)]
 | 
			
		||||
    pub fn get_access_circle_pk(access: &mut RhaiAccess) -> String {
 | 
			
		||||
        access.circle_pk.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "group_id", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "expires_at", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "modified_at", pure)]
 | 
			
		||||
    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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Register the database module globally
 | 
			
		||||
    engine.register_global_module(db_module.into());
 | 
			
		||||
 | 
			
		||||
    println!("Successfully registered access Rhai module using export_module approach.");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
use heromodels_core::BaseModelDataOps;
 | 
			
		||||
use heromodels_core::{BaseModelData, Index};
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
 | 
			
		||||
use rhai::{CustomType, EvalAltResult, Position, TypeBuilder}; // For #[derive(CustomType)]
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
// --- Enums ---
 | 
			
		||||
@@ -34,6 +34,26 @@ impl Default for BusinessType {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BusinessType {
 | 
			
		||||
    pub fn to_string(&self) -> String {
 | 
			
		||||
        format!("{:?}", self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn from_string(s: &str) -> Result<Self, Box<EvalAltResult>> {
 | 
			
		||||
        match s.to_lowercase().as_str() {
 | 
			
		||||
            "coop" => Ok(BusinessType::Coop),
 | 
			
		||||
            "single" => Ok(BusinessType::Single),
 | 
			
		||||
            "twin" => Ok(BusinessType::Twin),
 | 
			
		||||
            "starter" => Ok(BusinessType::Starter),
 | 
			
		||||
            "global" => Ok(BusinessType::Global),
 | 
			
		||||
            _ => Err(Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                format!("Invalid business type: {}", s).into(),
 | 
			
		||||
                Position::NONE,
 | 
			
		||||
            ))),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Company Struct ---
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, CustomType, Default)] // Added CustomType
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,4 @@ pub use product::{Product, ProductComponent, ProductStatus, ProductType};
 | 
			
		||||
pub use shareholder::{Shareholder, ShareholderType};
 | 
			
		||||
 | 
			
		||||
pub mod sale;
 | 
			
		||||
pub use sale::{Sale, SaleItem, SaleStatus};
 | 
			
		||||
 | 
			
		||||
// pub use user::{User}; // Assuming a simple User model for now
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "rhai")]
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
#[cfg(feature = "rhai")]
 | 
			
		||||
pub use rhai::register_biz_rhai_module;
 | 
			
		||||
pub use sale::{Sale, SaleItem, SaleStatus};
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +20,7 @@ pub enum ProductStatus {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProductComponent represents a component or sub-part of a product.
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, CustomType)]
 | 
			
		||||
pub struct ProductComponent {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,794 +0,0 @@
 | 
			
		||||
use crate::db::Collection; // For db.set and db.get_by_id
 | 
			
		||||
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::{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;
 | 
			
		||||
type RhaiShareholder = Shareholder;
 | 
			
		||||
type RhaiProduct = Product;
 | 
			
		||||
type RhaiProductComponent = ProductComponent;
 | 
			
		||||
type RhaiSale = Sale;
 | 
			
		||||
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(|_| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!("Failed to convert ID '{}' to u32", id_i64).into(),
 | 
			
		||||
            Position::NONE,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod rhai_biz_module {
 | 
			
		||||
    // --- Company Functions ---
 | 
			
		||||
    #[rhai_fn(name = "new_company")]
 | 
			
		||||
    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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // --- 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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        let owned_sale = mem::take(sale);
 | 
			
		||||
        *sale = owned_sale.items(items);
 | 
			
		||||
        Ok(sale.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sale getters
 | 
			
		||||
    #[rhai_fn(name = "get_sale_id")]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Register the database module globally
 | 
			
		||||
    engine.register_global_module(db_module.into());
 | 
			
		||||
 | 
			
		||||
    println!("Successfully registered biz Rhai module using export_module approach.");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use heromodels_core::{BaseModelData, BaseModelDataOps, Model};
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
/// Represents the status of a sale.
 | 
			
		||||
@@ -16,7 +17,7 @@ impl Default for SaleStatus {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents an individual item within a Sale.
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, CustomType)]
 | 
			
		||||
pub struct SaleItem {
 | 
			
		||||
    pub product_id: u32,
 | 
			
		||||
    pub name: String, // Denormalized product name at time of sale
 | 
			
		||||
@@ -72,7 +73,7 @@ impl SaleItem {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a sale of products or services.
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, CustomType)]
 | 
			
		||||
pub struct Sale {
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
    pub company_id: u32,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
// Export calendar module
 | 
			
		||||
pub mod calendar;
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
 | 
			
		||||
// Re-export Calendar, Event, Attendee, and AttendanceStatus from the inner calendar module (calendar.rs) within src/models/calendar/mod.rs
 | 
			
		||||
pub use self::calendar::{AttendanceStatus, Attendee, Calendar, Event};
 | 
			
		||||
pub use rhai::register_calendar_rhai_module;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,484 +0,0 @@
 | 
			
		||||
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::{AttendanceStatus, Attendee, Calendar, Event};
 | 
			
		||||
type RhaiEvent = Event;
 | 
			
		||||
type RhaiAttendee = Attendee;
 | 
			
		||||
type RhaiCalendar = Calendar;
 | 
			
		||||
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(|_| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!("Failed to convert ID '{}' to u32", id_i64).into(),
 | 
			
		||||
            Position::NONE,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod rhai_calendar_module {
 | 
			
		||||
    // --- Event Functions ---
 | 
			
		||||
    #[rhai_fn(name = "new_event")]
 | 
			
		||||
    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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        let owned_event = mem::take(event);
 | 
			
		||||
        *event = owned_event.location(location);
 | 
			
		||||
        Ok(event.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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>> {
 | 
			
		||||
        // Use take to get ownership of the event
 | 
			
		||||
        let owned_event = mem::take(event);
 | 
			
		||||
        *event = owned_event.add_attendee(attendee);
 | 
			
		||||
        Ok(event.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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>> {
 | 
			
		||||
        // 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,
 | 
			
		||||
            )));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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>> {
 | 
			
		||||
        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);
 | 
			
		||||
        Ok(event.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Event Getters
 | 
			
		||||
    #[rhai_fn(get = "id", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "modified_at", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "description", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "end_time", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "attendees", pure)]
 | 
			
		||||
    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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We now use with_status instead of update_attendee_status for consistency
 | 
			
		||||
 | 
			
		||||
    // Attendee Getters
 | 
			
		||||
    #[rhai_fn(get = "contact_id", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // --- Calendar Functions ---
 | 
			
		||||
    #[rhai_fn(name = "new_calendar", return_raw)]
 | 
			
		||||
    pub fn new_calendar() -> Result<RhaiCalendar, Box<EvalAltResult>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        // 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>> {
 | 
			
		||||
        // 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);
 | 
			
		||||
        *calendar = updated_calendar.clone();
 | 
			
		||||
        Ok(updated_calendar)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[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>> {
 | 
			
		||||
        // 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);
 | 
			
		||||
        Ok(calendar.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(name = "remove_event_from_calendar", return_raw)]
 | 
			
		||||
    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);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Calendar Getters
 | 
			
		||||
    #[rhai_fn(get = "id", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "created_at", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "events", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Register the database module globally
 | 
			
		||||
    engine.register_global_module(db_module.into());
 | 
			
		||||
 | 
			
		||||
    println!("Successfully registered calendar Rhai module using export_module approach.");
 | 
			
		||||
}
 | 
			
		||||
@@ -4,7 +4,7 @@ use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
/// Represents the visual theme for a circle.
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
 | 
			
		||||
pub struct ThemeData {
 | 
			
		||||
    pub primary_color: String,
 | 
			
		||||
    pub background_color: String,
 | 
			
		||||
@@ -15,20 +15,6 @@ pub struct ThemeData {
 | 
			
		||||
    pub nav_timeline_visible: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ThemeData {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            primary_color: "#3b82f6".to_string(),
 | 
			
		||||
            background_color: "#0a0a0a".to_string(),
 | 
			
		||||
            background_pattern: "none".to_string(),
 | 
			
		||||
            logo_symbol: "◯".to_string(),
 | 
			
		||||
            logo_url: "".to_string(),
 | 
			
		||||
            nav_dashboard_visible: true,
 | 
			
		||||
            nav_timeline_visible: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents an event in a calendar
 | 
			
		||||
#[model]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,21 @@ pub struct Contact {
 | 
			
		||||
    pub circle: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Contact {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Contact {
 | 
			
		||||
            base_data: BaseModelData::new(),
 | 
			
		||||
            name: String::new(),
 | 
			
		||||
            description: None,
 | 
			
		||||
            address: String::new(),
 | 
			
		||||
            phone: String::new(),
 | 
			
		||||
            email: String::new(),
 | 
			
		||||
            notes: None,
 | 
			
		||||
            circle: String::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Contact {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Contact {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
// Export contact module
 | 
			
		||||
pub mod contact;
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
 | 
			
		||||
// Re-export contact, Group from the inner contact module (contact.rs) within src/models/contact/mod.rs
 | 
			
		||||
pub use self::contact::{Contact, Group};
 | 
			
		||||
pub use rhai::register_contact_rhai_module;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,336 +0,0 @@
 | 
			
		||||
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::{Contact, Group};
 | 
			
		||||
type RhaiGroup = Group;
 | 
			
		||||
type RhaiContact = Contact;
 | 
			
		||||
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(|_| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!("Failed to convert ID '{}' to u32", id_i64).into(),
 | 
			
		||||
            Position::NONE,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod rhai_contact_module {
 | 
			
		||||
    // --- Event Functions ---
 | 
			
		||||
    #[rhai_fn(name = "new_group")]
 | 
			
		||||
    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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        let owned_group = mem::take(group);
 | 
			
		||||
        *group = owned_group.description(description);
 | 
			
		||||
        Ok(group.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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>> {
 | 
			
		||||
        // Use take to get ownership of the event
 | 
			
		||||
        let owned_group = mem::take(group);
 | 
			
		||||
        *group = owned_group.add_contact(contact_id as u32);
 | 
			
		||||
        Ok(group.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Group Getters
 | 
			
		||||
    #[rhai_fn(get = "id", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
    #[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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // --- Contact Functions ---
 | 
			
		||||
    #[rhai_fn(name = "new_contact", return_raw)]
 | 
			
		||||
    pub fn new_contact() -> Result<RhaiContact, Box<EvalAltResult>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        // 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>> {
 | 
			
		||||
        // 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);
 | 
			
		||||
        Ok(contact.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Contact Getters
 | 
			
		||||
    #[rhai_fn(get = "id", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "created_at", pure)]
 | 
			
		||||
    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 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Register the database module globally
 | 
			
		||||
    engine.register_global_module(db_module.into());
 | 
			
		||||
 | 
			
		||||
    println!("Successfully registered contact Rhai module using export_module approach.");
 | 
			
		||||
}
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
pub mod account;
 | 
			
		||||
pub mod asset;
 | 
			
		||||
pub mod marketplace;
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
 | 
			
		||||
// Re-export main models for easier access
 | 
			
		||||
pub use self::account::Account;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,973 +0,0 @@
 | 
			
		||||
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::{Bid, BidStatus, Listing, ListingStatus, ListingType};
 | 
			
		||||
 | 
			
		||||
use crate::db::hero::OurDB;
 | 
			
		||||
use crate::db::{Collection, Db};
 | 
			
		||||
 | 
			
		||||
type RhaiAccount = Account;
 | 
			
		||||
type RhaiAsset = Asset;
 | 
			
		||||
type RhaiListing = Listing;
 | 
			
		||||
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(|_| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!("Failed to convert ID '{}' to u32", id_i64).into(),
 | 
			
		||||
            Position::NONE,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper functions for enum conversions
 | 
			
		||||
 | 
			
		||||
// AssetType conversions
 | 
			
		||||
fn asset_type_to_string(asset_type: &AssetType) -> String {
 | 
			
		||||
    match asset_type {
 | 
			
		||||
        AssetType::Native => "Native".to_string(),
 | 
			
		||||
        AssetType::Erc20 => "Erc20".to_string(),
 | 
			
		||||
        AssetType::Erc721 => "Erc721".to_string(),
 | 
			
		||||
        AssetType::Erc1155 => "Erc1155".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn string_to_asset_type(s: &str) -> Result<AssetType, Box<EvalAltResult>> {
 | 
			
		||||
    match s {
 | 
			
		||||
        "Native" => Ok(AssetType::Native),
 | 
			
		||||
        "Erc20" => Ok(AssetType::Erc20),
 | 
			
		||||
        "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,
 | 
			
		||||
        ))),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListingStatus conversions
 | 
			
		||||
fn listing_status_to_string(status: &ListingStatus) -> String {
 | 
			
		||||
    match status {
 | 
			
		||||
        ListingStatus::Active => "Active".to_string(),
 | 
			
		||||
        ListingStatus::Sold => "Sold".to_string(),
 | 
			
		||||
        ListingStatus::Cancelled => "Cancelled".to_string(),
 | 
			
		||||
        ListingStatus::Expired => "Expired".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn string_to_listing_status(s: &str) -> Result<ListingStatus, Box<EvalAltResult>> {
 | 
			
		||||
    match s {
 | 
			
		||||
        "Active" => Ok(ListingStatus::Active),
 | 
			
		||||
        "Sold" => Ok(ListingStatus::Sold),
 | 
			
		||||
        "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,
 | 
			
		||||
        ))),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListingType conversions
 | 
			
		||||
fn listing_type_to_string(lt: &ListingType) -> String {
 | 
			
		||||
    match lt {
 | 
			
		||||
        ListingType::FixedPrice => "FixedPrice".to_string(),
 | 
			
		||||
        ListingType::Auction => "Auction".to_string(),
 | 
			
		||||
        ListingType::Exchange => "Exchange".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn string_to_listing_type(s: &str) -> Result<ListingType, Box<EvalAltResult>> {
 | 
			
		||||
    match s {
 | 
			
		||||
        "FixedPrice" => Ok(ListingType::FixedPrice),
 | 
			
		||||
        "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,
 | 
			
		||||
        ))),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BidStatus conversions
 | 
			
		||||
fn bid_status_to_string(status: &BidStatus) -> String {
 | 
			
		||||
    match status {
 | 
			
		||||
        BidStatus::Active => "Active".to_string(),
 | 
			
		||||
        BidStatus::Accepted => "Accepted".to_string(),
 | 
			
		||||
        BidStatus::Rejected => "Rejected".to_string(),
 | 
			
		||||
        BidStatus::Cancelled => "Cancelled".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn string_to_bid_status(s: &str) -> Result<BidStatus, Box<EvalAltResult>> {
 | 
			
		||||
    match s {
 | 
			
		||||
        "Active" => Ok(BidStatus::Active),
 | 
			
		||||
        "Accepted" => Ok(BidStatus::Accepted),
 | 
			
		||||
        "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,
 | 
			
		||||
        ))),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Account module
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod account_module {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    // Constructor
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn new_account() -> Result<RhaiAccount, Box<EvalAltResult>> {
 | 
			
		||||
        Ok(Account::new())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Getters
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_id(account: &mut RhaiAccount) -> INT {
 | 
			
		||||
        account.base_data.id as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_created_at(account: &mut RhaiAccount) -> INT {
 | 
			
		||||
        account.base_data.created_at
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_name(account: &mut RhaiAccount) -> String {
 | 
			
		||||
        account.name.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_user_id(account: &mut RhaiAccount) -> INT {
 | 
			
		||||
        account.user_id as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_description(account: &mut RhaiAccount) -> String {
 | 
			
		||||
        account.description.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_ledger(account: &mut RhaiAccount) -> String {
 | 
			
		||||
        account.ledger.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_address(account: &mut RhaiAccount) -> String {
 | 
			
		||||
        account.address.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_pubkey(account: &mut RhaiAccount) -> String {
 | 
			
		||||
        account.pubkey.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_assets(account: &mut RhaiAccount) -> Array {
 | 
			
		||||
        let mut assets_array = Array::new();
 | 
			
		||||
        for asset_id in &account.assets {
 | 
			
		||||
            assets_array.push(Dynamic::from(*asset_id as INT));
 | 
			
		||||
        }
 | 
			
		||||
        assets_array
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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>> {
 | 
			
		||||
        let mut acc = mem::take(account);
 | 
			
		||||
        acc = acc.name(name);
 | 
			
		||||
        *account = acc;
 | 
			
		||||
        Ok(account.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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);
 | 
			
		||||
        *account = acc;
 | 
			
		||||
        Ok(account.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn description(
 | 
			
		||||
        account: &mut RhaiAccount,
 | 
			
		||||
        description: String,
 | 
			
		||||
    ) -> Result<RhaiAccount, Box<EvalAltResult>> {
 | 
			
		||||
        let mut acc = mem::take(account);
 | 
			
		||||
        acc = acc.description(description);
 | 
			
		||||
        *account = acc;
 | 
			
		||||
        Ok(account.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn ledger(
 | 
			
		||||
        account: &mut RhaiAccount,
 | 
			
		||||
        ledger: String,
 | 
			
		||||
    ) -> Result<RhaiAccount, Box<EvalAltResult>> {
 | 
			
		||||
        let mut acc = mem::take(account);
 | 
			
		||||
        acc = acc.ledger(ledger);
 | 
			
		||||
        *account = acc;
 | 
			
		||||
        Ok(account.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn address(
 | 
			
		||||
        account: &mut RhaiAccount,
 | 
			
		||||
        address: String,
 | 
			
		||||
    ) -> Result<RhaiAccount, Box<EvalAltResult>> {
 | 
			
		||||
        let mut acc = mem::take(account);
 | 
			
		||||
        acc = acc.address(address);
 | 
			
		||||
        *account = acc;
 | 
			
		||||
        Ok(account.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn pubkey(
 | 
			
		||||
        account: &mut RhaiAccount,
 | 
			
		||||
        pubkey: String,
 | 
			
		||||
    ) -> Result<RhaiAccount, Box<EvalAltResult>> {
 | 
			
		||||
        let mut acc = mem::take(account);
 | 
			
		||||
        acc = acc.pubkey(pubkey);
 | 
			
		||||
        *account = acc;
 | 
			
		||||
        Ok(account.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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);
 | 
			
		||||
        *account = acc;
 | 
			
		||||
        Ok(account.clone())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Asset module
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod asset_module {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    // Constructor
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn new_asset() -> Result<RhaiAsset, Box<EvalAltResult>> {
 | 
			
		||||
        Ok(Asset::new())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Getters
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_id(asset: &mut RhaiAsset) -> INT {
 | 
			
		||||
        asset.base_data.id as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_created_at(asset: &mut RhaiAsset) -> INT {
 | 
			
		||||
        asset.base_data.created_at
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_name(asset: &mut RhaiAsset) -> String {
 | 
			
		||||
        asset.name.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_description(asset: &mut RhaiAsset) -> String {
 | 
			
		||||
        asset.description.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_amount(asset: &mut RhaiAsset) -> f64 {
 | 
			
		||||
        asset.amount
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_address(asset: &mut RhaiAsset) -> String {
 | 
			
		||||
        asset.address.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_asset_type(asset: &mut RhaiAsset) -> String {
 | 
			
		||||
        asset_type_to_string(&asset.asset_type)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_decimals(asset: &mut RhaiAsset) -> INT {
 | 
			
		||||
        asset.decimals as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Setters using builder pattern with proper mutability handling
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn name(asset: &mut RhaiAsset, name: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
 | 
			
		||||
        let mut ast = mem::take(asset);
 | 
			
		||||
        ast = ast.name(name);
 | 
			
		||||
        *asset = ast;
 | 
			
		||||
        Ok(asset.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn description(
 | 
			
		||||
        asset: &mut RhaiAsset,
 | 
			
		||||
        description: String,
 | 
			
		||||
    ) -> Result<RhaiAsset, Box<EvalAltResult>> {
 | 
			
		||||
        let mut ast = mem::take(asset);
 | 
			
		||||
        ast = ast.description(description);
 | 
			
		||||
        *asset = ast;
 | 
			
		||||
        Ok(asset.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn amount(asset: &mut RhaiAsset, amount: f64) -> Result<RhaiAsset, Box<EvalAltResult>> {
 | 
			
		||||
        let mut ast = mem::take(asset);
 | 
			
		||||
        ast = ast.amount(amount);
 | 
			
		||||
        *asset = ast;
 | 
			
		||||
        Ok(asset.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn address(
 | 
			
		||||
        asset: &mut RhaiAsset,
 | 
			
		||||
        address: String,
 | 
			
		||||
    ) -> Result<RhaiAsset, Box<EvalAltResult>> {
 | 
			
		||||
        let mut ast = mem::take(asset);
 | 
			
		||||
        ast = ast.address(address);
 | 
			
		||||
        *asset = ast;
 | 
			
		||||
        Ok(asset.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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);
 | 
			
		||||
        *asset = ast;
 | 
			
		||||
        Ok(asset.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn decimals(asset: &mut RhaiAsset, decimals: INT) -> Result<RhaiAsset, Box<EvalAltResult>> {
 | 
			
		||||
        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,
 | 
			
		||||
            )));
 | 
			
		||||
        }
 | 
			
		||||
        let mut ast = mem::take(asset);
 | 
			
		||||
        ast = ast.decimals(decimals as u8);
 | 
			
		||||
        *asset = ast;
 | 
			
		||||
        Ok(asset.clone())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
// Listing module
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod listing_module {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    // Constructor
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn new_listing() -> Result<RhaiListing, Box<EvalAltResult>> {
 | 
			
		||||
        Ok(Listing::new())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Getters
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_id(listing: &mut RhaiListing) -> INT {
 | 
			
		||||
        listing.base_data.id as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_created_at(listing: &mut RhaiListing) -> INT {
 | 
			
		||||
        listing.base_data.created_at
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_seller_id(listing: &mut RhaiListing) -> INT {
 | 
			
		||||
        // Parse the seller_id string to u32, then to INT
 | 
			
		||||
        listing.seller_id.parse::<u32>().unwrap_or(0) as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_asset_id(listing: &mut RhaiListing) -> INT {
 | 
			
		||||
        // Parse the asset_id string to u32, then to INT
 | 
			
		||||
        listing.asset_id.parse::<u32>().unwrap_or(0) as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_price(listing: &mut RhaiListing) -> f64 {
 | 
			
		||||
        listing.price
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_currency(listing: &mut RhaiListing) -> String {
 | 
			
		||||
        listing.currency.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_listing_type(listing: &mut RhaiListing) -> String {
 | 
			
		||||
        listing_type_to_string(&listing.listing_type)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_status(listing: &mut RhaiListing) -> String {
 | 
			
		||||
        listing_status_to_string(&listing.status)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_title(listing: &mut RhaiListing) -> String {
 | 
			
		||||
        listing.title.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_description(listing: &mut RhaiListing) -> String {
 | 
			
		||||
        listing.description.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_image_url(listing: &mut RhaiListing) -> String {
 | 
			
		||||
        listing.image_url.clone().unwrap_or_else(|| "".to_string())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_end_date(listing: &mut RhaiListing) -> INT {
 | 
			
		||||
        listing.expires_at.map_or(0, |d| d.timestamp())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_tags(listing: &mut RhaiListing) -> Array {
 | 
			
		||||
        let mut tags_array = Array::new();
 | 
			
		||||
        for tag in &listing.tags {
 | 
			
		||||
            tags_array.push(Dynamic::from(tag.clone()));
 | 
			
		||||
        }
 | 
			
		||||
        tags_array
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_bids(listing: &mut RhaiListing) -> Array {
 | 
			
		||||
        let mut bids_array = Array::new();
 | 
			
		||||
        for bid in &listing.bids {
 | 
			
		||||
            bids_array.push(Dynamic::from(bid.clone()));
 | 
			
		||||
        }
 | 
			
		||||
        bids_array
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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>> {
 | 
			
		||||
        let seller_id = id_from_i64_to_u32(seller_id)?;
 | 
			
		||||
        let mut lst = mem::take(listing);
 | 
			
		||||
        lst = lst.seller_id(seller_id);
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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);
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn price(listing: &mut RhaiListing, price: f64) -> Result<RhaiListing, Box<EvalAltResult>> {
 | 
			
		||||
        let mut lst = mem::take(listing);
 | 
			
		||||
        lst = lst.price(price);
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn currency(
 | 
			
		||||
        listing: &mut RhaiListing,
 | 
			
		||||
        currency: String,
 | 
			
		||||
    ) -> Result<RhaiListing, Box<EvalAltResult>> {
 | 
			
		||||
        let mut lst = mem::take(listing);
 | 
			
		||||
        lst = lst.currency(currency);
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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);
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn title(
 | 
			
		||||
        listing: &mut RhaiListing,
 | 
			
		||||
        title: String,
 | 
			
		||||
    ) -> Result<RhaiListing, Box<EvalAltResult>> {
 | 
			
		||||
        let mut lst = mem::take(listing);
 | 
			
		||||
        lst = lst.title(title);
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn description(
 | 
			
		||||
        listing: &mut RhaiListing,
 | 
			
		||||
        description: String,
 | 
			
		||||
    ) -> Result<RhaiListing, Box<EvalAltResult>> {
 | 
			
		||||
        let mut lst = mem::take(listing);
 | 
			
		||||
        lst = lst.description(description);
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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)
 | 
			
		||||
            .single()
 | 
			
		||||
            .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;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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,
 | 
			
		||||
                )))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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,
 | 
			
		||||
                )))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn cancel(listing: &mut RhaiListing) -> Result<RhaiListing, Box<EvalAltResult>> {
 | 
			
		||||
        let lst = mem::take(listing);
 | 
			
		||||
        match lst.clone().cancel() {
 | 
			
		||||
            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,
 | 
			
		||||
                )))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn check_expiration(listing: &mut RhaiListing) -> Result<RhaiListing, Box<EvalAltResult>> {
 | 
			
		||||
        let mut lst = mem::take(listing);
 | 
			
		||||
        lst = lst.check_expiration();
 | 
			
		||||
        *listing = lst;
 | 
			
		||||
        Ok(listing.clone())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bid module
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod bid_module {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    // Constructor
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn new_bid() -> Result<RhaiBid, Box<EvalAltResult>> {
 | 
			
		||||
        Ok(Bid::new())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Getters
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_listing_id(bid: &mut RhaiBid) -> String {
 | 
			
		||||
        bid.listing_id.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_bidder_id(bid: &mut RhaiBid) -> INT {
 | 
			
		||||
        bid.bidder_id as INT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_amount(bid: &mut RhaiBid) -> f64 {
 | 
			
		||||
        bid.amount
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_currency(bid: &mut RhaiBid) -> String {
 | 
			
		||||
        bid.currency.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_status(bid: &mut RhaiBid) -> String {
 | 
			
		||||
        bid_status_to_string(&bid.status)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn get_created_at(bid: &mut RhaiBid) -> INT {
 | 
			
		||||
        bid.created_at.timestamp()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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>> {
 | 
			
		||||
        let mut b = mem::take(bid);
 | 
			
		||||
        b = b.listing_id(listing_id);
 | 
			
		||||
        *bid = b;
 | 
			
		||||
        Ok(bid.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn bidder_id(bid: &mut RhaiBid, bidder_id: INT) -> Result<RhaiBid, Box<EvalAltResult>> {
 | 
			
		||||
        let bidder_id = id_from_i64_to_u32(bidder_id)?;
 | 
			
		||||
        let mut b = mem::take(bid);
 | 
			
		||||
        b = b.bidder_id(bidder_id);
 | 
			
		||||
        *bid = b;
 | 
			
		||||
        Ok(bid.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn amount(bid: &mut RhaiBid, amount: f64) -> Result<RhaiBid, Box<EvalAltResult>> {
 | 
			
		||||
        let mut b = mem::take(bid);
 | 
			
		||||
        b = b.amount(amount);
 | 
			
		||||
        *bid = b;
 | 
			
		||||
        Ok(bid.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    pub fn currency(bid: &mut RhaiBid, currency: String) -> Result<RhaiBid, Box<EvalAltResult>> {
 | 
			
		||||
        let mut b = mem::take(bid);
 | 
			
		||||
        b = b.currency(currency);
 | 
			
		||||
        *bid = b;
 | 
			
		||||
        Ok(bid.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(return_raw, global)]
 | 
			
		||||
    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);
 | 
			
		||||
        *bid = b;
 | 
			
		||||
        Ok(bid.clone())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
use rhai::ImmutableString;
 | 
			
		||||
 | 
			
		||||
// Register all Rhai functions for the finance module
 | 
			
		||||
pub fn register_finance_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
 | 
			
		||||
    // --- Register model-specific modules with the engine ---
 | 
			
		||||
    let account_module = exported_module!(account_module);
 | 
			
		||||
    engine.register_global_module(account_module.into());
 | 
			
		||||
 | 
			
		||||
    let asset_module = exported_module!(asset_module);
 | 
			
		||||
    engine.register_global_module(asset_module.into());
 | 
			
		||||
 | 
			
		||||
    let listing_module = exported_module!(listing_module);
 | 
			
		||||
    engine.register_global_module(listing_module.into());
 | 
			
		||||
 | 
			
		||||
    let bid_module = exported_module!(bid_module);
 | 
			
		||||
    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("asset_type_to_str", self::asset_type_to_string);
 | 
			
		||||
    engine.register_fn("str_to_listing_status", |s: ImmutableString| {
 | 
			
		||||
        self::string_to_listing_status(s.as_str())
 | 
			
		||||
    });
 | 
			
		||||
    engine.register_fn("listing_status_to_str", self::listing_status_to_string);
 | 
			
		||||
    engine.register_fn("str_to_listing_type", |s: ImmutableString| {
 | 
			
		||||
        self::string_to_listing_type(s.as_str())
 | 
			
		||||
    });
 | 
			
		||||
    engine.register_fn("listing_type_to_str", self::listing_type_to_string);
 | 
			
		||||
    engine.register_fn("str_to_bid_status", |s: ImmutableString| {
 | 
			
		||||
        self::string_to_bid_status(s.as_str())
 | 
			
		||||
    });
 | 
			
		||||
    engine.register_fn("bid_status_to_str", self::bid_status_to_string);
 | 
			
		||||
 | 
			
		||||
    // --- Database interaction functions (registered in a separate db_module) ---
 | 
			
		||||
    let mut db_module = Module::new();
 | 
			
		||||
 | 
			
		||||
    // 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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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.");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
use super::flow_step::FlowStep;
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
/// Represents a signing flow.
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default)]
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default, CustomType)]
 | 
			
		||||
#[model]
 | 
			
		||||
pub struct Flow {
 | 
			
		||||
    /// Base model data (id, created_at, updated_at).
 | 
			
		||||
        #[rhai_type(skip)]
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
 | 
			
		||||
    /// A unique UUID for the flow, for external reference.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::default::Default;
 | 
			
		||||
 | 
			
		||||
/// Represents a step within a signing flow.
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, CustomType)]
 | 
			
		||||
#[model]
 | 
			
		||||
pub struct FlowStep {
 | 
			
		||||
    /// Base model data.
 | 
			
		||||
        #[rhai_type(skip)]
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
 | 
			
		||||
    /// Optional description for the step.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,9 @@
 | 
			
		||||
// Export flow model submodules
 | 
			
		||||
pub mod flow;
 | 
			
		||||
pub mod flow_step;
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
pub mod signature_requirement;
 | 
			
		||||
 | 
			
		||||
// Re-export key types for convenience
 | 
			
		||||
pub use flow::Flow;
 | 
			
		||||
pub use flow_step::FlowStep;
 | 
			
		||||
pub use rhai::register_flow_rhai_module;
 | 
			
		||||
pub use signature_requirement::SignatureRequirement;
 | 
			
		||||
pub use signature_requirement::SignatureRequirement;
 | 
			
		||||
@@ -1,535 +0,0 @@
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
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;
 | 
			
		||||
use super::signature_requirement::SignatureRequirement;
 | 
			
		||||
type RhaiFlow = Flow;
 | 
			
		||||
type RhaiFlowStep = FlowStep;
 | 
			
		||||
type RhaiSignatureRequirement = SignatureRequirement;
 | 
			
		||||
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(|_| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!("Failed to convert ID '{}' to u32", id_i64).into(),
 | 
			
		||||
            Position::NONE,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod rhai_flow_module {
 | 
			
		||||
    // --- Flow Functions ---
 | 
			
		||||
    #[rhai_fn(name = "new_flow")]
 | 
			
		||||
    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>> {
 | 
			
		||||
        let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
 | 
			
		||||
        *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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "created_at", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "flow_uuid", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "status", pure)]
 | 
			
		||||
    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>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // --- FlowStep Functions ---
 | 
			
		||||
    #[rhai_fn(global)]
 | 
			
		||||
    pub fn new_flow_step(step_order_i64: i64) -> Dynamic {
 | 
			
		||||
        match id_from_i64_to_u32(step_order_i64) {
 | 
			
		||||
            Ok(step_order) => {
 | 
			
		||||
                let mut flow_step = FlowStep::default();
 | 
			
		||||
                flow_step.step_order = step_order;
 | 
			
		||||
                Dynamic::from(flow_step)
 | 
			
		||||
            }
 | 
			
		||||
            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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "created_at", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "description", pure)]
 | 
			
		||||
    pub fn get_step_description(step: &mut RhaiFlowStep) -> Dynamic {
 | 
			
		||||
        match &step.description {
 | 
			
		||||
            Some(desc) => Dynamic::from(desc.clone()),
 | 
			
		||||
            None => Dynamic::UNIT,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "step_order", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // --- 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 {
 | 
			
		||||
        match id_from_i64_to_u32(flow_step_id_i64) {
 | 
			
		||||
            Ok(flow_step_id) => {
 | 
			
		||||
                let mut signature_requirement = SignatureRequirement::default();
 | 
			
		||||
                signature_requirement.flow_step_id = flow_step_id;
 | 
			
		||||
                signature_requirement.public_key = public_key;
 | 
			
		||||
                signature_requirement.message = message;
 | 
			
		||||
                Dynamic::from(signature_requirement)
 | 
			
		||||
            }
 | 
			
		||||
            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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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>> {
 | 
			
		||||
        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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "created_at", pure)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "flow_step_id", pure)]
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "message", pure)]
 | 
			
		||||
    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 {
 | 
			
		||||
            Some(signed_by) => Dynamic::from(signed_by.clone()),
 | 
			
		||||
            None => Dynamic::UNIT,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "signature", pure)]
 | 
			
		||||
    pub fn get_sr_signature(sr: &mut RhaiSignatureRequirement) -> Dynamic {
 | 
			
		||||
        match &sr.signature {
 | 
			
		||||
            Some(signature) => Dynamic::from(signature.clone()),
 | 
			
		||||
            None => Dynamic::UNIT,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "status", pure)]
 | 
			
		||||
    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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                ))
 | 
			
		||||
            })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                ))
 | 
			
		||||
            })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                ))
 | 
			
		||||
            })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // 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.");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::default::Default;
 | 
			
		||||
 | 
			
		||||
/// Represents a signature requirement for a flow step.
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, CustomType)]
 | 
			
		||||
#[model]
 | 
			
		||||
pub struct SignatureRequirement {
 | 
			
		||||
    /// Base model data.
 | 
			
		||||
        #[rhai_type(skip)]
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
 | 
			
		||||
    /// Foreign key to the FlowStep this requirement belongs to.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
pub mod contract;
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
 | 
			
		||||
pub use contract::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
 | 
			
		||||
pub use rhai::register_legal_rhai_module;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,613 +0,0 @@
 | 
			
		||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module, NativeCallContext, Position};
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use crate::db::hero::OurDB; // Updated path based on compiler suggestion
 | 
			
		||||
// use heromodels_core::BaseModelData; // Removed as fields are accessed via contract.base_data directly
 | 
			
		||||
use crate::models::legal::{
 | 
			
		||||
    Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::db::Collection; // Import the Collection trait
 | 
			
		||||
 | 
			
		||||
// --- Helper Functions for ID and Timestamp Conversion ---
 | 
			
		||||
fn i64_to_u32(
 | 
			
		||||
    val: i64,
 | 
			
		||||
    context_pos: Position,
 | 
			
		||||
    field_name: &str,
 | 
			
		||||
    object_name: &str,
 | 
			
		||||
) -> Result<u32, Box<EvalAltResult>> {
 | 
			
		||||
    val.try_into().map_err(|_e| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!(
 | 
			
		||||
                "Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u32",
 | 
			
		||||
                field_name, object_name, val
 | 
			
		||||
            ),
 | 
			
		||||
            context_pos,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn i64_to_u64(
 | 
			
		||||
    val: i64,
 | 
			
		||||
    context_pos: Position,
 | 
			
		||||
    field_name: &str,
 | 
			
		||||
    object_name: &str,
 | 
			
		||||
) -> Result<u64, Box<EvalAltResult>> {
 | 
			
		||||
    val.try_into().map_err(|_e| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!(
 | 
			
		||||
                "Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u64",
 | 
			
		||||
                field_name, object_name, val
 | 
			
		||||
            ),
 | 
			
		||||
            context_pos,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn i64_to_i32(
 | 
			
		||||
    val: i64,
 | 
			
		||||
    context_pos: Position,
 | 
			
		||||
    field_name: &str,
 | 
			
		||||
    object_name: &str,
 | 
			
		||||
) -> Result<i32, Box<EvalAltResult>> {
 | 
			
		||||
    val.try_into().map_err(|_e| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!(
 | 
			
		||||
                "Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to i32",
 | 
			
		||||
                field_name, object_name, val
 | 
			
		||||
            ),
 | 
			
		||||
            context_pos,
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn register_legal_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
 | 
			
		||||
    // --- ContractStatus Enum ---
 | 
			
		||||
    // Register ContractStatus enum as constants
 | 
			
		||||
    let mut contract_status_module = Module::new();
 | 
			
		||||
    contract_status_module.set_var("Draft", ContractStatus::Draft);
 | 
			
		||||
    contract_status_module.set_var("PendingSignatures", ContractStatus::PendingSignatures);
 | 
			
		||||
    contract_status_module.set_var("Signed", ContractStatus::Signed);
 | 
			
		||||
    contract_status_module.set_var("Active", ContractStatus::Active);
 | 
			
		||||
    contract_status_module.set_var("Expired", ContractStatus::Expired);
 | 
			
		||||
    contract_status_module.set_var("Cancelled", ContractStatus::Cancelled);
 | 
			
		||||
    engine.register_static_module("ContractStatusConstants", contract_status_module.into());
 | 
			
		||||
    engine.register_type_with_name::<ContractStatus>("ContractStatus"); // Expose the type itself
 | 
			
		||||
 | 
			
		||||
    // Register SignerStatus enum as constants
 | 
			
		||||
    let mut signer_status_module = Module::new();
 | 
			
		||||
    signer_status_module.set_var("Pending", SignerStatus::Pending);
 | 
			
		||||
    signer_status_module.set_var("Signed", SignerStatus::Signed);
 | 
			
		||||
    signer_status_module.set_var("Rejected", SignerStatus::Rejected);
 | 
			
		||||
    engine.register_static_module("SignerStatusConstants", signer_status_module.into());
 | 
			
		||||
    engine.register_type_with_name::<SignerStatus>("SignerStatus"); // Expose the type itself
 | 
			
		||||
 | 
			
		||||
    // --- ContractRevision ---
 | 
			
		||||
    engine.register_type_with_name::<ContractRevision>("ContractRevision");
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "new_contract_revision",
 | 
			
		||||
        move |context: NativeCallContext,
 | 
			
		||||
              version_i64: i64,
 | 
			
		||||
              content: String,
 | 
			
		||||
              created_at_i64: i64,
 | 
			
		||||
              created_by: String|
 | 
			
		||||
              -> Result<ContractRevision, Box<EvalAltResult>> {
 | 
			
		||||
            let version = i64_to_u32(
 | 
			
		||||
                version_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "version",
 | 
			
		||||
                "new_contract_revision",
 | 
			
		||||
            )?;
 | 
			
		||||
            let created_at = i64_to_u64(
 | 
			
		||||
                created_at_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "created_at",
 | 
			
		||||
                "new_contract_revision",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(ContractRevision::new(
 | 
			
		||||
                version, content, created_at, created_by,
 | 
			
		||||
            ))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "comments",
 | 
			
		||||
        |mut revision: ContractRevision, comments: String| -> ContractRevision {
 | 
			
		||||
            revision.comments = Some(comments);
 | 
			
		||||
            revision
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "version",
 | 
			
		||||
        |revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(revision.version as i64)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "content",
 | 
			
		||||
        |revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(revision.content.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "created_at",
 | 
			
		||||
        |revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(revision.created_at as i64)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "created_by",
 | 
			
		||||
        |revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(revision.created_by.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "comments",
 | 
			
		||||
        |revision: &mut ContractRevision| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(revision
 | 
			
		||||
                .comments
 | 
			
		||||
                .clone()
 | 
			
		||||
                .map_or(Dynamic::UNIT, Dynamic::from))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // --- ContractSigner ---
 | 
			
		||||
    engine.register_type_with_name::<ContractSigner>("ContractSigner");
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "new_contract_signer",
 | 
			
		||||
        |id: String, name: String, email: String| -> ContractSigner {
 | 
			
		||||
            ContractSigner::new(id, name, email)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "status",
 | 
			
		||||
        |signer: ContractSigner, status: SignerStatus| -> ContractSigner { signer.status(status) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "signed_at",
 | 
			
		||||
        |context: NativeCallContext,
 | 
			
		||||
         signer: ContractSigner,
 | 
			
		||||
         signed_at_i64: i64|
 | 
			
		||||
         -> Result<ContractSigner, Box<EvalAltResult>> {
 | 
			
		||||
            let signed_at_u64 = i64_to_u64(
 | 
			
		||||
                signed_at_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "signed_at",
 | 
			
		||||
                "ContractSigner.signed_at",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(signer.signed_at(signed_at_u64))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "clear_signed_at",
 | 
			
		||||
        |signer: ContractSigner| -> ContractSigner { signer.clear_signed_at() },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "comments",
 | 
			
		||||
        |signer: ContractSigner, comments: String| -> ContractSigner { signer.comments(comments) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "clear_comments",
 | 
			
		||||
        |signer: ContractSigner| -> ContractSigner { signer.clear_comments() },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "id",
 | 
			
		||||
        |signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(signer.id.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "name",
 | 
			
		||||
        |signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(signer.name.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "email",
 | 
			
		||||
        |signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(signer.email.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "status",
 | 
			
		||||
        |signer: &mut ContractSigner| -> Result<SignerStatus, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(signer.status.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "signed_at_ts",
 | 
			
		||||
        |signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(signer
 | 
			
		||||
                .signed_at
 | 
			
		||||
                .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "comments",
 | 
			
		||||
        |signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(signer.comments.clone().map_or(Dynamic::UNIT, Dynamic::from))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "signed_at",
 | 
			
		||||
        |signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(signer
 | 
			
		||||
                .signed_at
 | 
			
		||||
                .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts)))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // --- Contract ---
 | 
			
		||||
    engine.register_type_with_name::<Contract>("Contract");
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "new_contract",
 | 
			
		||||
        move |context: NativeCallContext,
 | 
			
		||||
              base_id_i64: i64,
 | 
			
		||||
              contract_id: String|
 | 
			
		||||
              -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let base_id = i64_to_u32(
 | 
			
		||||
                base_id_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "base_id",
 | 
			
		||||
                "new_contract",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(Contract::new(base_id, contract_id))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Builder methods
 | 
			
		||||
    engine.register_fn("title", |contract: Contract, title: String| -> Contract {
 | 
			
		||||
        contract.title(title)
 | 
			
		||||
    });
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "description",
 | 
			
		||||
        |contract: Contract, description: String| -> Contract { contract.description(description) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "contract_type",
 | 
			
		||||
        |contract: Contract, contract_type: String| -> Contract {
 | 
			
		||||
            contract.contract_type(contract_type)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "status",
 | 
			
		||||
        |contract: Contract, status: ContractStatus| -> Contract { contract.status(status) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "created_by",
 | 
			
		||||
        |contract: Contract, created_by: String| -> Contract { contract.created_by(created_by) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "terms_and_conditions",
 | 
			
		||||
        |contract: Contract, terms: String| -> Contract { contract.terms_and_conditions(terms) },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "start_date",
 | 
			
		||||
        |context: NativeCallContext,
 | 
			
		||||
         contract: Contract,
 | 
			
		||||
         start_date_i64: i64|
 | 
			
		||||
         -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let start_date_u64 = i64_to_u64(
 | 
			
		||||
                start_date_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "start_date",
 | 
			
		||||
                "Contract.start_date",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(contract.start_date(start_date_u64))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn("clear_start_date", |contract: Contract| -> Contract {
 | 
			
		||||
        contract.clear_start_date()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "end_date",
 | 
			
		||||
        |context: NativeCallContext,
 | 
			
		||||
         contract: Contract,
 | 
			
		||||
         end_date_i64: i64|
 | 
			
		||||
         -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let end_date_u64 = i64_to_u64(
 | 
			
		||||
                end_date_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "end_date",
 | 
			
		||||
                "Contract.end_date",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(contract.end_date(end_date_u64))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn("clear_end_date", |contract: Contract| -> Contract {
 | 
			
		||||
        contract.clear_end_date()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "renewal_period_days",
 | 
			
		||||
        |context: NativeCallContext,
 | 
			
		||||
         contract: Contract,
 | 
			
		||||
         days_i64: i64|
 | 
			
		||||
         -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let days_i32 = i64_to_i32(
 | 
			
		||||
                days_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "renewal_period_days",
 | 
			
		||||
                "Contract.renewal_period_days",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(contract.renewal_period_days(days_i32))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "clear_renewal_period_days",
 | 
			
		||||
        |contract: Contract| -> Contract { contract.clear_renewal_period_days() },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "next_renewal_date",
 | 
			
		||||
        |context: NativeCallContext,
 | 
			
		||||
         contract: Contract,
 | 
			
		||||
         date_i64: i64|
 | 
			
		||||
         -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let date_u64 = i64_to_u64(
 | 
			
		||||
                date_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "next_renewal_date",
 | 
			
		||||
                "Contract.next_renewal_date",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(contract.next_renewal_date(date_u64))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "clear_next_renewal_date",
 | 
			
		||||
        |contract: Contract| -> Contract { contract.clear_next_renewal_date() },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "add_signer",
 | 
			
		||||
        |contract: Contract, signer: ContractSigner| -> Contract { contract.add_signer(signer) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "signers",
 | 
			
		||||
        |contract: Contract, signers_array: Array| -> Contract {
 | 
			
		||||
            let signers_vec = signers_array
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .filter_map(|s| s.try_cast::<ContractSigner>())
 | 
			
		||||
                .collect();
 | 
			
		||||
            contract.signers(signers_vec)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "add_revision",
 | 
			
		||||
        |contract: Contract, revision: ContractRevision| -> Contract {
 | 
			
		||||
            contract.add_revision(revision)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "revisions",
 | 
			
		||||
        |contract: Contract, revisions_array: Array| -> Contract {
 | 
			
		||||
            let revisions_vec = revisions_array
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .filter_map(|r| r.try_cast::<ContractRevision>())
 | 
			
		||||
                .collect();
 | 
			
		||||
            contract.revisions(revisions_vec)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "current_version",
 | 
			
		||||
        |context: NativeCallContext,
 | 
			
		||||
         contract: Contract,
 | 
			
		||||
         version_i64: i64|
 | 
			
		||||
         -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let version_u32 = i64_to_u32(
 | 
			
		||||
                version_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "current_version",
 | 
			
		||||
                "Contract.current_version",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(contract.current_version(version_u32))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "last_signed_date",
 | 
			
		||||
        |context: NativeCallContext,
 | 
			
		||||
         contract: Contract,
 | 
			
		||||
         date_i64: i64|
 | 
			
		||||
         -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let date_u64 = i64_to_u64(
 | 
			
		||||
                date_i64,
 | 
			
		||||
                context.position(),
 | 
			
		||||
                "last_signed_date",
 | 
			
		||||
                "Contract.last_signed_date",
 | 
			
		||||
            )?;
 | 
			
		||||
            Ok(contract.last_signed_date(date_u64))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn("clear_last_signed_date", |contract: Contract| -> Contract {
 | 
			
		||||
        contract.clear_last_signed_date()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Getters for Contract
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "id",
 | 
			
		||||
        |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.base_data.id as i64)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "created_at_ts",
 | 
			
		||||
        |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.base_data.created_at as i64)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "updated_at_ts",
 | 
			
		||||
        |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.base_data.modified_at as i64)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "contract_id",
 | 
			
		||||
        |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.contract_id.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "title",
 | 
			
		||||
        |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.title.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "description",
 | 
			
		||||
        |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.description.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "contract_type",
 | 
			
		||||
        |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.contract_type.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "status",
 | 
			
		||||
        |contract: &mut Contract| -> Result<ContractStatus, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.status.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "created_by",
 | 
			
		||||
        |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.created_by.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "terms_and_conditions",
 | 
			
		||||
        |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.terms_and_conditions.clone())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "start_date",
 | 
			
		||||
        |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract
 | 
			
		||||
                .start_date
 | 
			
		||||
                .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "end_date",
 | 
			
		||||
        |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract
 | 
			
		||||
                .end_date
 | 
			
		||||
                .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "renewal_period_days",
 | 
			
		||||
        |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract
 | 
			
		||||
                .renewal_period_days
 | 
			
		||||
                .map_or(Dynamic::UNIT, |days| Dynamic::from(days as i64)))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "next_renewal_date",
 | 
			
		||||
        |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract
 | 
			
		||||
                .next_renewal_date
 | 
			
		||||
                .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "last_signed_date",
 | 
			
		||||
        |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract
 | 
			
		||||
                .last_signed_date
 | 
			
		||||
                .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "current_version",
 | 
			
		||||
        |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(contract.current_version as i64)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "signers",
 | 
			
		||||
        |contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
 | 
			
		||||
            let rhai_array = contract
 | 
			
		||||
                .signers
 | 
			
		||||
                .iter()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .map(Dynamic::from)
 | 
			
		||||
                .collect::<Array>();
 | 
			
		||||
            Ok(rhai_array)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "revisions",
 | 
			
		||||
        |contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
 | 
			
		||||
            let rhai_array = contract
 | 
			
		||||
                .revisions
 | 
			
		||||
                .iter()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .map(Dynamic::from)
 | 
			
		||||
                .collect::<Array>();
 | 
			
		||||
            Ok(rhai_array)
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Method set_status
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "set_contract_status",
 | 
			
		||||
        |contract: &mut Contract, status: ContractStatus| {
 | 
			
		||||
            contract.set_status(status);
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // --- Database Interaction ---
 | 
			
		||||
    let captured_db_for_set = Arc::clone(&db);
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "set_contract",
 | 
			
		||||
        move |contract: Contract| -> Result<(), Box<EvalAltResult>> {
 | 
			
		||||
            captured_db_for_set.set(&contract).map(|_| ()).map_err(|e| {
 | 
			
		||||
                Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                    format!(
 | 
			
		||||
                        "Failed to set Contract (ID: {}): {:?}",
 | 
			
		||||
                        contract.base_data.id, e
 | 
			
		||||
                    )
 | 
			
		||||
                    .into(),
 | 
			
		||||
                    Position::NONE,
 | 
			
		||||
                ))
 | 
			
		||||
            })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let captured_db_for_get = Arc::clone(&db);
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "get_contract_by_id",
 | 
			
		||||
        move |context: NativeCallContext, id_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
 | 
			
		||||
            let id_u32 = i64_to_u32(id_i64, context.position(), "id", "get_contract_by_id")?;
 | 
			
		||||
 | 
			
		||||
            captured_db_for_get
 | 
			
		||||
                .get_by_id(id_u32)
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                        format!("Error getting Contract (ID: {}): {}", id_u32, e).into(),
 | 
			
		||||
                        Position::NONE,
 | 
			
		||||
                    ))
 | 
			
		||||
                })?
 | 
			
		||||
                .ok_or_else(|| {
 | 
			
		||||
                    Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                        format!("Contract with ID {} not found", id_u32).into(),
 | 
			
		||||
                        Position::NONE,
 | 
			
		||||
                    ))
 | 
			
		||||
                })
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
@@ -28,15 +28,4 @@ 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};
 | 
			
		||||
 | 
			
		||||
#[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 projects::register_projects_rhai_module;
 | 
			
		||||
pub use library::items::{Image, Markdown, Pdf};
 | 
			
		||||
@@ -1,16 +1,13 @@
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use crate::db::{hero::OurDB, Collection, Db};
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
// Temporarily removed to fix compilation issues
 | 
			
		||||
// use rhai_autobind_macros::rhai_model_export;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use rhai::CustomType;
 | 
			
		||||
use rhailib_derive::RhaiApi;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use rhai::TypeBuilder;
 | 
			
		||||
 | 
			
		||||
/// Represents an event in a contact
 | 
			
		||||
#[model]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default, RhaiApi)]
 | 
			
		||||
pub struct Object {
 | 
			
		||||
    /// Base model data
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,3 @@ pub use task_enums::*;
 | 
			
		||||
// pub use kanban::*;
 | 
			
		||||
// pub use sprint::*;
 | 
			
		||||
// pub use story::*;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "rhai")]
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "rhai")]
 | 
			
		||||
pub use rhai::register_projects_rhai_module;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,408 +0,0 @@
 | 
			
		||||
// heromodels/src/models/projects/rhai.rs
 | 
			
		||||
 | 
			
		||||
use crate::db::hero::OurDB;
 | 
			
		||||
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::{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>> {
 | 
			
		||||
    if val < 0 {
 | 
			
		||||
        Err(EvalAltResult::ErrorArithmetic(
 | 
			
		||||
            format!("ID value cannot be negative: {}", val),
 | 
			
		||||
            rhai::Position::NONE,
 | 
			
		||||
        )
 | 
			
		||||
        .into())
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok(val as u32)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
 | 
			
		||||
    // Register enums as constants (example for Priority)
 | 
			
		||||
    engine.register_static_module("Priority", {
 | 
			
		||||
        let mut module = rhai::Module::new();
 | 
			
		||||
        module.set_var("Critical", Priority::Critical);
 | 
			
		||||
        module.set_var("High", Priority::High);
 | 
			
		||||
        module.set_var("Medium", Priority::Medium);
 | 
			
		||||
        module.set_var("Low", Priority::Low);
 | 
			
		||||
        module.set_var("None", Priority::None);
 | 
			
		||||
        module.into()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    engine.register_static_module("Status", {
 | 
			
		||||
        let mut module = rhai::Module::new();
 | 
			
		||||
        module.set_var("Todo", Status::Todo);
 | 
			
		||||
        module.set_var("InProgress", Status::InProgress);
 | 
			
		||||
        module.set_var("Review", Status::Review);
 | 
			
		||||
        module.set_var("Done", Status::Done);
 | 
			
		||||
        module.set_var("Archived", Status::Archived);
 | 
			
		||||
        module.into()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    engine.register_static_module("ItemType", {
 | 
			
		||||
        let mut module = rhai::Module::new();
 | 
			
		||||
        module.set_var("Epic", ItemType::Epic);
 | 
			
		||||
        module.set_var("Story", ItemType::Story);
 | 
			
		||||
        module.set_var("Task", ItemType::Task);
 | 
			
		||||
        module.set_var("Bug", ItemType::Bug);
 | 
			
		||||
        module.set_var("Improvement", ItemType::Improvement);
 | 
			
		||||
        module.set_var("Feature", ItemType::Feature);
 | 
			
		||||
        module.into()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // --- Enum Type Registration ---
 | 
			
		||||
    engine.register_type_with_name::<Priority>("Priority");
 | 
			
		||||
    engine.register_fn("to_string", |p: &mut Priority| ToString::to_string(p));
 | 
			
		||||
 | 
			
		||||
    engine.register_type_with_name::<Status>("Status");
 | 
			
		||||
    engine.register_fn("to_string", |s: &mut Status| ToString::to_string(s));
 | 
			
		||||
 | 
			
		||||
    engine.register_type_with_name::<ItemType>("ItemType");
 | 
			
		||||
    engine.register_fn("to_string", |it: &mut ItemType| ToString::to_string(it));
 | 
			
		||||
 | 
			
		||||
    // --- Project Registration ---
 | 
			
		||||
    engine.register_type_with_name::<Project>("Project");
 | 
			
		||||
 | 
			
		||||
    // Constructor for Project
 | 
			
		||||
    // Zero-argument constructor
 | 
			
		||||
    engine.register_fn("new_project", || -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
        // Assuming Project::new() or Project::default() can be used.
 | 
			
		||||
        // If Project::new() requires args, this needs adjustment or Project needs Default impl.
 | 
			
		||||
        Ok(Project::new(0, "".to_string(), "".to_string(), 0))
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Multi-argument constructor (renamed)
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "new_project_with_details",
 | 
			
		||||
        |id_i64: i64,
 | 
			
		||||
         name: String,
 | 
			
		||||
         description: String,
 | 
			
		||||
         owner_id_i64: i64|
 | 
			
		||||
         -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(Project::new(
 | 
			
		||||
                id_from_i64(id_i64)?,
 | 
			
		||||
                name,
 | 
			
		||||
                description,
 | 
			
		||||
                id_from_i64(owner_id_i64)?,
 | 
			
		||||
            ))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Getters for Project
 | 
			
		||||
    engine.register_get("id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> {
 | 
			
		||||
        Ok(p.get_id() as i64)
 | 
			
		||||
    });
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "name",
 | 
			
		||||
        |p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.name.clone()) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "description",
 | 
			
		||||
        |p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.description.clone()) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "owner_id",
 | 
			
		||||
        |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.owner_id as i64) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "member_ids",
 | 
			
		||||
        |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.member_ids
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|&id| rhai::Dynamic::from(id as i64))
 | 
			
		||||
                .collect())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "board_ids",
 | 
			
		||||
        |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.board_ids
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|&id| rhai::Dynamic::from(id as i64))
 | 
			
		||||
                .collect())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "sprint_ids",
 | 
			
		||||
        |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.sprint_ids
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|&id| rhai::Dynamic::from(id as i64))
 | 
			
		||||
                .collect())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "epic_ids",
 | 
			
		||||
        |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.epic_ids
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|&id| rhai::Dynamic::from(id as i64))
 | 
			
		||||
                .collect())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "tags",
 | 
			
		||||
        |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.tags
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|tag| rhai::Dynamic::from(tag.clone()))
 | 
			
		||||
                .collect())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "created_at",
 | 
			
		||||
        |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.created_at) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "modified_at",
 | 
			
		||||
        |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.modified_at) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "comments",
 | 
			
		||||
        |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.base_data
 | 
			
		||||
                .comments
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|&id| rhai::Dynamic::from(id as i64))
 | 
			
		||||
                .collect())
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "status",
 | 
			
		||||
        |p: &mut Project| -> Result<Status, Box<EvalAltResult>> { Ok(p.status.clone()) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "priority",
 | 
			
		||||
        |p: &mut Project| -> Result<Priority, Box<EvalAltResult>> { Ok(p.priority.clone()) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_get(
 | 
			
		||||
        "item_type",
 | 
			
		||||
        |p: &mut Project| -> Result<ItemType, Box<EvalAltResult>> { Ok(p.item_type.clone()) },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Builder methods for Project
 | 
			
		||||
    // let db_clone = db.clone(); // This was unused
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "name",
 | 
			
		||||
        |p: Project, name: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.name(name)) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "description",
 | 
			
		||||
        |p: Project, description: String| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.description(description))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "owner_id",
 | 
			
		||||
        |p: Project, owner_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.owner_id(id_from_i64(owner_id_i64)?))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "add_member_id",
 | 
			
		||||
        |p: Project, member_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.add_member_id(id_from_i64(member_id_i64)?))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "member_ids",
 | 
			
		||||
        |p: Project, member_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            let ids = member_ids_i64
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|id_dyn: Dynamic| {
 | 
			
		||||
                    let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
 | 
			
		||||
                        Box::new(EvalAltResult::ErrorMismatchDataType(
 | 
			
		||||
                            "Expected integer for ID".to_string(),
 | 
			
		||||
                            id_dyn.type_name().to_string(),
 | 
			
		||||
                            Position::NONE,
 | 
			
		||||
                        ))
 | 
			
		||||
                    })?;
 | 
			
		||||
                    id_from_i64(val_i64)
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
 | 
			
		||||
            Ok(p.member_ids(ids))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "add_board_id",
 | 
			
		||||
        |p: Project, board_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.add_board_id(id_from_i64(board_id_i64)?))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "board_ids",
 | 
			
		||||
        |p: Project, board_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            let ids = board_ids_i64
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|id_dyn: Dynamic| {
 | 
			
		||||
                    let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
 | 
			
		||||
                        Box::new(EvalAltResult::ErrorMismatchDataType(
 | 
			
		||||
                            "Expected integer for ID".to_string(),
 | 
			
		||||
                            id_dyn.type_name().to_string(),
 | 
			
		||||
                            Position::NONE,
 | 
			
		||||
                        ))
 | 
			
		||||
                    })?;
 | 
			
		||||
                    id_from_i64(val_i64)
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
 | 
			
		||||
            Ok(p.board_ids(ids))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "add_sprint_id",
 | 
			
		||||
        |p: Project, sprint_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.add_sprint_id(id_from_i64(sprint_id_i64)?))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "sprint_ids",
 | 
			
		||||
        |p: Project, sprint_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            let ids = sprint_ids_i64
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|id_dyn: Dynamic| {
 | 
			
		||||
                    let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
 | 
			
		||||
                        Box::new(EvalAltResult::ErrorMismatchDataType(
 | 
			
		||||
                            "Expected integer for ID".to_string(),
 | 
			
		||||
                            id_dyn.type_name().to_string(),
 | 
			
		||||
                            Position::NONE,
 | 
			
		||||
                        ))
 | 
			
		||||
                    })?;
 | 
			
		||||
                    id_from_i64(val_i64)
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
 | 
			
		||||
            Ok(p.sprint_ids(ids))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "add_epic_id",
 | 
			
		||||
        |p: Project, epic_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.add_epic_id(id_from_i64(epic_id_i64)?))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "epic_ids",
 | 
			
		||||
        |p: Project, epic_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            let ids = epic_ids_i64
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|id_dyn: Dynamic| {
 | 
			
		||||
                    let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
 | 
			
		||||
                        Box::new(EvalAltResult::ErrorMismatchDataType(
 | 
			
		||||
                            "Expected integer for ID".to_string(),
 | 
			
		||||
                            id_dyn.type_name().to_string(),
 | 
			
		||||
                            Position::NONE,
 | 
			
		||||
                        ))
 | 
			
		||||
                    })?;
 | 
			
		||||
                    id_from_i64(val_i64)
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
 | 
			
		||||
            Ok(p.epic_ids(ids))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "add_tag",
 | 
			
		||||
        |p: Project, tag: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_tag(tag)) },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "tags",
 | 
			
		||||
        |p: Project, tags_dyn: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            let tags_vec = tags_dyn
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|tag_dyn: Dynamic| {
 | 
			
		||||
                    tag_dyn.clone().into_string().map_err(|_err| {
 | 
			
		||||
                        // _err is Rhai's internal error, we create a new one
 | 
			
		||||
                        Box::new(EvalAltResult::ErrorMismatchDataType(
 | 
			
		||||
                            "Expected string for tag".to_string(),
 | 
			
		||||
                            tag_dyn.type_name().to_string(),
 | 
			
		||||
                            Position::NONE,
 | 
			
		||||
                        ))
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<String>, Box<EvalAltResult>>>()?;
 | 
			
		||||
            Ok(p.tags(tags_vec))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "status",
 | 
			
		||||
        |p: Project, status: Status| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.status(status))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "priority",
 | 
			
		||||
        |p: Project, priority: Priority| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.priority(priority))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "item_type",
 | 
			
		||||
        |p: Project, item_type: ItemType| -> Result<Project, Box<EvalAltResult>> {
 | 
			
		||||
            Ok(p.item_type(item_type))
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    // Base ModelData builders
 | 
			
		||||
    engine.register_fn(
 | 
			
		||||
        "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(),
 | 
			
		||||
                ))
 | 
			
		||||
            })?;
 | 
			
		||||
 | 
			
		||||
            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(),
 | 
			
		||||
                ))
 | 
			
		||||
            })?;
 | 
			
		||||
 | 
			
		||||
            match collection.get_by_id(id) {
 | 
			
		||||
                Ok(Some(project)) => Ok(Dynamic::from(project)),
 | 
			
		||||
                Ok(None) => Ok(Dynamic::UNIT), // Represents '()' in Rhai
 | 
			
		||||
                Err(e) => Err(Box::new(EvalAltResult::ErrorSystem(
 | 
			
		||||
                    "Failed to retrieve project by ID".to_string(),
 | 
			
		||||
                    format!("DB operation failed: {:?}", e).into(),
 | 
			
		||||
                ))),
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // TODO: Register Rhai bindings for the `Label` model if needed, or remove unused import.
 | 
			
		||||
    // Register Label type and its methods/getters
 | 
			
		||||
    // engine.register_type_with_name::<Label>("Label")
 | 
			
		||||
    //     .register_fn("new_label", Label::new) // Simplified
 | 
			
		||||
    //     // ... other Label methods and getters ...
 | 
			
		||||
    //     ;
 | 
			
		||||
 | 
			
		||||
    // TODO: Add DB interaction functions like set_project, get_project_by_id etc.
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user