biz rhai wrapper module wip

This commit is contained in:
Timur Gordon 2025-04-20 01:52:12 +02:00
parent dc8c887026
commit 0ea836894b
8 changed files with 3263 additions and 0 deletions

2164
herodb/src/models/biz/rhai/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
[package]
name = "biz_rhai"
version = "0.1.0"
edition = "2021"
[dependencies]
rhai = "1.21.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = "0.4"
herodb = { path = "../../../.." }
[lib]
name = "biz_rhai"
path = "src/lib.rs"
[[example]]
name = "example"
path = "examples/example.rs"

View File

@ -0,0 +1,168 @@
// Example script demonstrating the use of Business module operations
// Custom repeat function since Rhai doesn't have a built-in repeat method
fn repeat_str(str, count) {
let result = "";
for i in 0..count {
result += str;
}
return result;
}
// Print a section header
fn print_section(title) {
let line = repeat_str("=", 50);
print(line);
print(" " + title);
print(line);
}
print_section("BUSINESS MODULE OPERATIONS EXAMPLE");
// Currency Operations
print_section("CURRENCY OPERATIONS");
// Create a currency
print("\nCreating currencies...");
let usd = create_currency(100.0, "USD");
print("USD Currency created: " + usd.amount + " " + usd.currency_code);
// Convert currencies
print("\nConverting currencies...");
let eur = create_currency(150.0, "EUR");
print("EUR Currency created: " + eur.amount + " " + eur.currency_code);
let eur_to_usd = convert_currency(eur, "USD");
print("EUR to USD: " + eur.amount + " EUR = " + eur_to_usd.amount + " USD");
// Product Component Operations
print_section("PRODUCT COMPONENT OPERATIONS");
// Create a product component
print("\nCreating product components...");
let component1 = create_product_component(1, "CPU", "Intel i7 Processor", 1);
print("Component created: " + component1.name);
print(" ID: " + component1.id);
print(" Description: " + component1.description);
print(" Quantity: " + component1.quantity);
// Create another component
let component2 = create_product_component(2, "RAM", "32GB DDR4 Memory", 2);
print("Component created: " + component2.name);
print(" ID: " + component2.id);
print(" Description: " + component2.description);
print(" Quantity: " + component2.quantity);
// Product Operations
print_section("PRODUCT OPERATIONS");
// Create a product using builder pattern
print("\nCreating a product...");
let product = create_product_builder()
.product_name("High-End Gaming PC")
.product_description("Ultimate gaming experience")
.product_price(create_currency(1999.99, "USD"))
.product_validity_days(365)
.build();
print("Product created: " + product.name);
print(" ID: " + product.id);
print(" Description: " + product.description);
print(" Price: " + product.price.amount + " " + product.price.currency_code);
// Add components to the product
print("\nAdding components to product...");
let product_with_components = product
.add_component(component1)
.add_component(component2);
print("Components added to product");
// Get components from the product
let components = get_product_components(product_with_components);
print("Number of components: " + components.len());
// Customer Operations
print_section("CUSTOMER OPERATIONS");
// Create a customer
print("\nCreating a customer...");
let customer = create_customer("John Doe", "john@example.com", "+1234567890", "123 Main St");
print("Customer created: " + customer.name);
print(" Email: " + customer.email);
print(" Phone: " + customer.phone);
print(" Address: " + customer.address);
// Sale Operations
print_section("SALE OPERATIONS");
// Create a sale
print("\nCreating a sale...");
let sale = create_sale(customer, "2025-04-19");
print("Sale created for customer: " + sale.customer.name);
print(" Date: " + sale.date);
// Add product to sale
let sale_with_item = add_sale_item(sale, product_with_components, 1);
print("Added product to sale: " + product_with_components.name);
// Service Operations
print_section("SERVICE OPERATIONS");
// Create a service
print("\nCreating a service...");
let service = create_service(
"Premium Support",
"24/7 Technical Support",
create_currency(49.99, "USD"),
30
);
print("Service created: " + service.name);
print(" Description: " + service.description);
print(" Price: " + service.price.amount + " " + service.price.currency_code);
print(" Duration: " + service.duration + " days");
// Contract Operations
print_section("CONTRACT OPERATIONS");
// Create a contract
print("\nCreating a contract...");
let contract = create_contract(
"Support Agreement",
"Annual support contract",
"2025-04-19",
"2026-04-19",
create_currency(599.99, "USD"),
"Active"
);
print("Contract created: " + contract.title);
print(" Description: " + contract.description);
print(" Start Date: " + contract.start_date);
print(" End Date: " + contract.end_date);
print(" Value: " + contract.value.amount + " " + contract.value.currency_code);
print(" Status: " + contract.status);
// Invoice Operations
print_section("INVOICE OPERATIONS");
// Create an invoice
print("\nCreating an invoice...");
let invoice = create_invoice(
"INV-2025-001",
"2025-04-19",
"2025-05-19",
customer,
create_currency(2499.99, "USD"),
"Issued",
"Pending"
);
print("Invoice created: " + invoice.number);
print(" Date: " + invoice.date);
print(" Due Date: " + invoice.due_date);
print(" Customer: " + invoice.customer.name);
print(" Amount: " + invoice.amount.amount + " " + invoice.amount.currency_code);
print(" Status: " + invoice.status);
print(" Payment Status: " + invoice.payment_status);
print_section("EXAMPLE COMPLETED");
print("All business module operations completed successfully!");

View File

@ -0,0 +1,41 @@
use std::{fs, path::Path};
use biz_rhai::create_rhai_engine;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Business Module Rhai Wrapper Example ===");
// Create a Rhai engine with business module functionality
let mut engine = create_rhai_engine();
println!("Successfully created Rhai engine");
// Get the path to the example.rhai script
let script_path = get_script_path()?;
println!("Loading script from: {}", script_path.display());
// Load the script content
let script = fs::read_to_string(&script_path)
.map_err(|e| format!("Failed to read script file: {}", e))?;
// Run the script
println!("\n=== Running Rhai script ===");
match engine.eval::<()>(&script) {
Ok(_) => println!("\nScript executed successfully!"),
Err(e) => println!("\nScript execution error: {}", e),
}
println!("\nExample completed!");
Ok(())
}
fn get_script_path() -> Result<std::path::PathBuf, String> {
// When running with cargo run --example, the script will be in the examples directory
let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("examples")
.join("example.rhai");
if script_path.exists() {
Ok(script_path)
} else {
Err(format!("Could not find example.rhai script at {}", script_path.display()))
}
}

View File

@ -0,0 +1,88 @@
use rhai::{Engine, EvalAltResult, Map, Dynamic, Array};
use crate::wrapper::*;
use crate::generic_wrapper::ToRhai;
/// Create a new Rhai engine with business module functionality
pub fn create_rhai_engine() -> Engine {
let mut engine = Engine::new();
// Register business module types and functions
register_business_types(&mut engine);
engine
}
/// Register business module types and functions
fn register_business_types(engine: &mut Engine) {
// Currency functions
engine.register_fn("create_currency", create_currency);
engine.register_fn("convert_currency", convert_currency);
// Product functions
engine.register_fn("create_product_builder", create_product_builder);
engine.register_fn("product_name", product_builder_name);
engine.register_fn("product_description", product_builder_description);
engine.register_fn("product_price", product_builder_price);
engine.register_fn("product_validity_days", product_builder_validity_days);
engine.register_fn("add_component", product_builder_add_component);
engine.register_fn("build", product_builder_build);
// Product component functions
engine.register_fn("create_product_component", create_product_component);
engine.register_fn("component_name", product_component_name);
engine.register_fn("component_description", product_component_description);
engine.register_fn("component_quantity", product_component_quantity);
// Sale functions
engine.register_fn("create_sale", create_sale);
engine.register_fn("add_sale_item", add_sale_item);
engine.register_fn("sale_customer", sale_customer);
engine.register_fn("sale_date", sale_date);
engine.register_fn("sale_status", sale_status);
// Customer functions
engine.register_fn("create_customer", create_customer);
engine.register_fn("customer_name", customer_name);
engine.register_fn("customer_email", customer_email);
engine.register_fn("customer_phone", customer_phone);
engine.register_fn("customer_address", customer_address);
// Service functions
engine.register_fn("create_service", create_service);
engine.register_fn("service_name", service_name);
engine.register_fn("service_description", service_description);
engine.register_fn("service_price", service_price);
engine.register_fn("service_duration", service_duration);
// Contract functions
engine.register_fn("create_contract", create_contract);
engine.register_fn("contract_title", contract_title);
engine.register_fn("contract_description", contract_description);
engine.register_fn("contract_start_date", contract_start_date);
engine.register_fn("contract_end_date", contract_end_date);
engine.register_fn("contract_value", contract_value);
engine.register_fn("contract_status", contract_status);
// Invoice functions
engine.register_fn("create_invoice", create_invoice);
engine.register_fn("invoice_number", invoice_number);
engine.register_fn("invoice_date", invoice_date);
engine.register_fn("invoice_due_date", invoice_due_date);
engine.register_fn("invoice_customer", invoice_customer);
engine.register_fn("invoice_amount", invoice_amount);
engine.register_fn("invoice_status", invoice_status);
engine.register_fn("invoice_payment_status", invoice_payment_status);
// Helper function to get components from a product
engine.register_fn("get_product_components", |product_map: Map| -> Array {
let mut array = Array::new();
if let Some(components) = product_map.get("components") {
if let Some(components_array) = components.clone().try_cast::<Array>() {
return components_array;
}
}
array
});
}

View File

@ -0,0 +1,132 @@
use std::collections::HashMap;
use rhai::{Dynamic, Map, Array};
/// Local wrapper trait for sal::rhai::ToRhai to avoid orphan rule violations
pub trait ToRhai {
/// Convert to a Rhai Dynamic value
fn to_rhai(&self) -> Dynamic;
}
// Implementation of ToRhai for Dynamic
impl ToRhai for Dynamic {
fn to_rhai(&self) -> Dynamic {
self.clone()
}
}
/// Generic trait for wrapping Rust functions to be used with Rhai
pub trait RhaiWrapper {
/// Wrap a function that takes ownership of self
fn wrap_consuming<F, R>(self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(Self) -> R,
R: ToRhai;
/// Wrap a function that takes a mutable reference to self
fn wrap_mut<F, R>(&mut self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&mut Self) -> R,
R: ToRhai;
/// Wrap a function that takes an immutable reference to self
fn wrap<F, R>(&self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&Self) -> R,
R: ToRhai;
}
/// Implementation of RhaiWrapper for any type
impl<T> RhaiWrapper for T {
fn wrap_consuming<F, R>(self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(Self) -> R,
R: ToRhai,
{
let result = f(self);
result.to_rhai()
}
fn wrap_mut<F, R>(&mut self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&mut Self) -> R,
R: ToRhai,
{
let result = f(self);
result.to_rhai()
}
fn wrap<F, R>(&self, f: F) -> Dynamic
where
Self: Sized + Clone,
F: FnOnce(&Self) -> R,
R: ToRhai,
{
let result = f(self);
result.to_rhai()
}
}
/// Convert a Rhai Map to a Rust HashMap
pub fn map_to_hashmap(map: &Map) -> HashMap<String, String> {
let mut result = HashMap::new();
for (key, value) in map.iter() {
let k = key.clone().to_string();
let v = value.clone().to_string();
if !k.is_empty() && !v.is_empty() {
result.insert(k, v);
}
}
result
}
/// Convert a HashMap<String, String> to a Rhai Map
pub fn hashmap_to_map(map: &HashMap<String, String>) -> Map {
let mut result = Map::new();
for (key, value) in map.iter() {
result.insert(key.clone().into(), Dynamic::from(value.clone()));
}
result
}
/// Convert a Rhai Array to a Vec of strings
pub fn array_to_vec_string(array: &Array) -> Vec<String> {
array.iter()
.filter_map(|item| {
let s = item.clone().to_string();
if !s.is_empty() { Some(s) } else { None }
})
.collect()
}
/// Helper function to convert Dynamic to Option<String>
pub fn dynamic_to_string_option(value: &Dynamic) -> Option<String> {
if value.is_string() {
Some(value.clone().to_string())
} else {
None
}
}
/// Helper function to convert Dynamic to Option<u32>
pub fn dynamic_to_u32_option(value: &Dynamic) -> Option<u32> {
if value.is_int() {
Some(value.as_int().unwrap() as u32)
} else {
None
}
}
/// Helper function to convert Dynamic to Option<&str> with lifetime management
pub fn dynamic_to_str_option<'a>(value: &Dynamic, storage: &'a mut String) -> Option<&'a str> {
if value.is_string() {
*storage = value.clone().to_string();
Some(storage.as_str())
} else {
None
}
}

View File

@ -0,0 +1,11 @@
// Re-export the utility modules
pub mod generic_wrapper;
pub mod wrapper;
pub mod engine;
// Re-export the utility traits and functions
pub use generic_wrapper::{RhaiWrapper, map_to_hashmap, array_to_vec_string,
dynamic_to_string_option, hashmap_to_map};
pub use engine::create_rhai_engine;
// The create_rhai_engine function is now in the engine module

View File

@ -0,0 +1,640 @@
//! Rhai wrappers for Business module functions
//!
//! This module provides Rhai wrappers for the functions in the Business module.
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map, Position};
use std::collections::HashMap;
use crate::generic_wrapper::{ToRhai, RhaiWrapper, map_to_hashmap, dynamic_to_string_option, dynamic_to_u32_option};
// Import business module types
use chrono::{DateTime, Utc, Duration};
use herodb::models::biz::{
Currency, CurrencyBuilder,
Product, ProductBuilder, ProductComponent, ProductComponentBuilder, ProductType, ProductStatus,
Customer, CustomerBuilder,
Sale, SaleBuilder, SaleItem, SaleItemBuilder, SaleStatus,
Service, ServiceBuilder, ServiceItem, ServiceItemBuilder, ServiceStatus, BillingFrequency,
ExchangeRate, ExchangeRateBuilder, ExchangeRateService, EXCHANGE_RATE_SERVICE,
Contract, ContractBuilder, ContractStatus,
Invoice, InvoiceBuilder, InvoiceItem, InvoiceItemBuilder, InvoiceStatus, PaymentStatus, Payment
};
// Business module ToRhai implementations
// Currency ToRhai implementation
impl ToRhai for Currency {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("amount".into(), Dynamic::from(self.amount));
map.insert("currency_code".into(), Dynamic::from(self.currency_code.clone()));
Dynamic::from_map(map)
}
}
// ProductType ToRhai implementation
impl ToRhai for ProductType {
fn to_rhai(&self) -> Dynamic {
let value = match self {
ProductType::Product => "Product",
ProductType::Service => "Service",
};
Dynamic::from(value)
}
}
// ProductStatus ToRhai implementation
impl ToRhai for ProductStatus {
fn to_rhai(&self) -> Dynamic {
let value = match self {
ProductStatus::Active => "Active",
ProductStatus::Error => "Error",
ProductStatus::EndOfLife => "EndOfLife",
ProductStatus::Paused => "Paused",
ProductStatus::Available => "Available",
ProductStatus::Unavailable => "Unavailable",
};
Dynamic::from(value)
}
}
// ProductComponent ToRhai implementation
impl ToRhai for ProductComponent {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("name".into(), Dynamic::from(self.name.clone()));
map.insert("description".into(), Dynamic::from(self.description.clone()));
map.insert("quantity".into(), Dynamic::from(self.quantity));
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
map.insert("energy_usage".into(), Dynamic::from(self.energy_usage));
map.insert("cost".into(), self.cost.to_rhai());
Dynamic::from_map(map)
}
}
// Product ToRhai implementation
impl ToRhai for Product {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("name".into(), Dynamic::from(self.name.clone()));
map.insert("description".into(), Dynamic::from(self.description.clone()));
map.insert("price".into(), self.price.to_rhai());
map.insert("type".into(), self.type_.to_rhai());
map.insert("category".into(), Dynamic::from(self.category.clone()));
map.insert("status".into(), self.status.to_rhai());
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
map.insert("max_amount".into(), Dynamic::from(self.max_amount));
map.insert("purchase_till".into(), Dynamic::from(self.purchase_till.to_string()));
map.insert("active_till".into(), Dynamic::from(self.active_till.to_string()));
// Convert components to an array
let components_array: Array = self.components.iter()
.map(|component| component.to_rhai())
.collect();
map.insert("components".into(), Dynamic::from(components_array));
Dynamic::from_map(map)
}
}
// SaleStatus ToRhai implementation
impl ToRhai for SaleStatus {
fn to_rhai(&self) -> Dynamic {
let value = match self {
SaleStatus::Pending => "Pending",
SaleStatus::Completed => "Completed",
SaleStatus::Cancelled => "Cancelled",
SaleStatus::Refunded => "Refunded",
};
Dynamic::from(value)
}
}
// SaleItem ToRhai implementation
impl ToRhai for SaleItem {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("product_id".into(), Dynamic::from(self.product_id));
map.insert("quantity".into(), Dynamic::from(self.quantity));
map.insert("price".into(), self.price.to_rhai());
map.insert("discount".into(), Dynamic::from(self.discount));
Dynamic::from_map(map)
}
}
// Sale ToRhai implementation
impl ToRhai for Sale {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("customer_id".into(), Dynamic::from(self.customer_id));
map.insert("date".into(), Dynamic::from(self.date.to_string()));
map.insert("status".into(), self.status.to_rhai());
// Convert items to an array
let items_array: Array = self.items.iter()
.map(|item| item.to_rhai())
.collect();
map.insert("items".into(), Dynamic::from(items_array));
map.insert("total".into(), self.total.to_rhai());
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
Dynamic::from_map(map)
}
}
// Customer ToRhai implementation
impl ToRhai for Customer {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("name".into(), Dynamic::from(self.name.clone()));
map.insert("email".into(), Dynamic::from(self.email.clone()));
map.insert("phone".into(), Dynamic::from(self.phone.clone()));
map.insert("address".into(), Dynamic::from(self.address.clone()));
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
Dynamic::from_map(map)
}
}
// ServiceStatus ToRhai implementation
impl ToRhai for ServiceStatus {
fn to_rhai(&self) -> Dynamic {
let value = match self {
ServiceStatus::Active => "Active",
ServiceStatus::Inactive => "Inactive",
ServiceStatus::Pending => "Pending",
ServiceStatus::Cancelled => "Cancelled",
};
Dynamic::from(value)
}
}
// BillingFrequency ToRhai implementation
impl ToRhai for BillingFrequency {
fn to_rhai(&self) -> Dynamic {
let value = match self {
BillingFrequency::OneTime => "OneTime",
BillingFrequency::Daily => "Daily",
BillingFrequency::Weekly => "Weekly",
BillingFrequency::Monthly => "Monthly",
BillingFrequency::Quarterly => "Quarterly",
BillingFrequency::Yearly => "Yearly",
};
Dynamic::from(value)
}
}
// ServiceItem ToRhai implementation
impl ToRhai for ServiceItem {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("name".into(), Dynamic::from(self.name.clone()));
map.insert("description".into(), Dynamic::from(self.description.clone()));
map.insert("price".into(), self.price.to_rhai());
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
Dynamic::from_map(map)
}
}
// Service ToRhai implementation
impl ToRhai for Service {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("customer_id".into(), Dynamic::from(self.customer_id));
map.insert("name".into(), Dynamic::from(self.name.clone()));
map.insert("description".into(), Dynamic::from(self.description.clone()));
map.insert("status".into(), self.status.to_rhai());
map.insert("start_date".into(), Dynamic::from(self.start_date.to_string()));
map.insert("end_date".into(), Dynamic::from(self.end_date.to_string()));
map.insert("billing_frequency".into(), self.billing_frequency.to_rhai());
// Convert items to an array
let items_array: Array = self.items.iter()
.map(|item| item.to_rhai())
.collect();
map.insert("items".into(), Dynamic::from(items_array));
map.insert("total".into(), self.total.to_rhai());
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
Dynamic::from_map(map)
}
}
// ContractStatus ToRhai implementation
impl ToRhai for ContractStatus {
fn to_rhai(&self) -> Dynamic {
let value = match self {
ContractStatus::Draft => "Draft",
ContractStatus::Pending => "Pending",
ContractStatus::Active => "Active",
ContractStatus::Completed => "Completed",
ContractStatus::Cancelled => "Cancelled",
ContractStatus::Expired => "Expired",
};
Dynamic::from(value)
}
}
// Contract ToRhai implementation
impl ToRhai for Contract {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("customer_id".into(), Dynamic::from(self.customer_id));
map.insert("title".into(), Dynamic::from(self.title.clone()));
map.insert("description".into(), Dynamic::from(self.description.clone()));
map.insert("status".into(), self.status.to_rhai());
map.insert("start_date".into(), Dynamic::from(self.start_date.to_string()));
map.insert("end_date".into(), Dynamic::from(self.end_date.to_string()));
map.insert("value".into(), self.value.to_rhai());
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
Dynamic::from_map(map)
}
}
// InvoiceStatus ToRhai implementation
impl ToRhai for InvoiceStatus {
fn to_rhai(&self) -> Dynamic {
let value = match self {
InvoiceStatus::Draft => "Draft",
InvoiceStatus::Sent => "Sent",
InvoiceStatus::Paid => "Paid",
InvoiceStatus::Overdue => "Overdue",
InvoiceStatus::Cancelled => "Cancelled",
};
Dynamic::from(value)
}
}
// PaymentStatus ToRhai implementation
impl ToRhai for PaymentStatus {
fn to_rhai(&self) -> Dynamic {
let value = match self {
PaymentStatus::Pending => "Pending",
PaymentStatus::Completed => "Completed",
PaymentStatus::Failed => "Failed",
PaymentStatus::Refunded => "Refunded",
};
Dynamic::from(value)
}
}
// Payment ToRhai implementation
impl ToRhai for Payment {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("amount".into(), self.amount.to_rhai());
map.insert("date".into(), Dynamic::from(self.date.to_string()));
map.insert("method".into(), Dynamic::from(self.method.clone()));
map.insert("status".into(), self.status.to_rhai());
map.insert("reference".into(), Dynamic::from(self.reference.clone()));
Dynamic::from_map(map)
}
}
// InvoiceItem ToRhai implementation
impl ToRhai for InvoiceItem {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("description".into(), Dynamic::from(self.description.clone()));
map.insert("quantity".into(), Dynamic::from(self.quantity));
map.insert("unit_price".into(), self.unit_price.to_rhai());
map.insert("total".into(), self.total.to_rhai());
Dynamic::from_map(map)
}
}
// Invoice ToRhai implementation
impl ToRhai for Invoice {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("id".into(), Dynamic::from(self.id));
map.insert("customer_id".into(), Dynamic::from(self.customer_id));
map.insert("date".into(), Dynamic::from(self.date.to_string()));
map.insert("due_date".into(), Dynamic::from(self.due_date.to_string()));
map.insert("status".into(), self.status.to_rhai());
// Convert items to an array
let items_array: Array = self.items.iter()
.map(|item| item.to_rhai())
.collect();
map.insert("items".into(), Dynamic::from(items_array));
// Convert payments to an array
let payments_array: Array = self.payments.iter()
.map(|payment| payment.to_rhai())
.collect();
map.insert("payments".into(), Dynamic::from(payments_array));
map.insert("subtotal".into(), self.subtotal.to_rhai());
map.insert("tax".into(), self.tax.to_rhai());
map.insert("total".into(), self.total.to_rhai());
map.insert("created_at".into(), Dynamic::from(self.created_at.to_string()));
map.insert("updated_at".into(), Dynamic::from(self.updated_at.to_string()));
Dynamic::from_map(map)
}
}
// ExchangeRate ToRhai implementation
impl ToRhai for ExchangeRate {
fn to_rhai(&self) -> Dynamic {
let mut map = Map::new();
map.insert("from_currency".into(), Dynamic::from(self.from_currency.clone()));
map.insert("to_currency".into(), Dynamic::from(self.to_currency.clone()));
map.insert("rate".into(), Dynamic::from(self.rate));
map.insert("date".into(), Dynamic::from(self.date.to_string()));
Dynamic::from_map(map)
}
}
//
// Business Module Function Wrappers
//
// Currency Functions
pub fn currency_new(amount: f64, currency_code: &str) -> Currency {
Currency::new(amount, currency_code.to_string())
}
pub fn currency_to_usd(currency: &Currency) -> Result<Dynamic, Box<EvalAltResult>> {
match currency.to_usd() {
Some(usd) => Ok(usd.to_rhai()),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
"Failed to convert currency to USD".into(),
Position::NONE
)))
}
}
pub fn currency_to_currency(currency: &Currency, target_currency: &str) -> Result<Dynamic, Box<EvalAltResult>> {
match currency.to_currency(target_currency) {
Some(converted) => Ok(converted.to_rhai()),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to convert currency to {}", target_currency).into(),
Position::NONE
)))
}
}
// CurrencyBuilder Functions
pub fn currency_builder_new() -> CurrencyBuilder {
CurrencyBuilder::new()
}
pub fn currency_builder_amount(builder: CurrencyBuilder, amount: f64) -> CurrencyBuilder {
builder.amount(amount)
}
pub fn currency_builder_currency_code(builder: CurrencyBuilder, currency_code: &str) -> CurrencyBuilder {
builder.currency_code(currency_code)
}
pub fn currency_builder_build(builder: CurrencyBuilder) -> Result<Currency, Box<EvalAltResult>> {
builder.build().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
e.into(),
Position::NONE
))
})
}
// ProductComponent Functions
pub fn product_component_new(id: i64, name: &str, description: &str, quantity: i64) -> ProductComponent {
ProductComponent::new(id as u32, name.to_string(), description.to_string(), quantity as i32)
}
pub fn product_component_total_energy_usage(component: &ProductComponent) -> f64 {
component.total_energy_usage()
}
pub fn product_component_total_cost(component: &ProductComponent) -> Currency {
component.total_cost()
}
// ProductComponentBuilder Functions
pub fn product_component_builder_new() -> ProductComponentBuilder {
ProductComponentBuilder::new()
}
pub fn product_component_builder_id(builder: ProductComponentBuilder, id: i64) -> ProductComponentBuilder {
builder.id(id as u32)
}
pub fn product_component_builder_name(builder: ProductComponentBuilder, name: &str) -> ProductComponentBuilder {
builder.name(name)
}
pub fn product_component_builder_description(builder: ProductComponentBuilder, description: &str) -> ProductComponentBuilder {
builder.description(description)
}
pub fn product_component_builder_quantity(builder: ProductComponentBuilder, quantity: i64) -> ProductComponentBuilder {
builder.quantity(quantity as i32)
}
pub fn product_component_builder_energy_usage(builder: ProductComponentBuilder, energy_usage: f64) -> ProductComponentBuilder {
builder.energy_usage(energy_usage)
}
pub fn product_component_builder_cost(builder: ProductComponentBuilder, cost: Currency) -> ProductComponentBuilder {
builder.cost(cost)
}
pub fn product_component_builder_build(builder: ProductComponentBuilder) -> Result<ProductComponent, Box<EvalAltResult>> {
builder.build().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
e.into(),
Position::NONE
))
})
}
// Product Functions
pub fn product_type_product() -> ProductType {
ProductType::Product
}
pub fn product_type_service() -> ProductType {
ProductType::Service
}
pub fn product_status_active() -> ProductStatus {
ProductStatus::Active
}
pub fn product_status_error() -> ProductStatus {
ProductStatus::Error
}
pub fn product_status_end_of_life() -> ProductStatus {
ProductStatus::EndOfLife
}
pub fn product_status_paused() -> ProductStatus {
ProductStatus::Paused
}
pub fn product_status_available() -> ProductStatus {
ProductStatus::Available
}
pub fn product_status_unavailable() -> ProductStatus {
ProductStatus::Unavailable
}
pub fn product_add_component(product: &mut Product, component: ProductComponent) {
product.add_component(component);
}
pub fn product_set_purchase_period(product: &mut Product, purchase_till_days: i64) {
let purchase_till = Utc::now() + Duration::days(purchase_till_days);
product.set_purchase_period(purchase_till);
}
pub fn product_set_active_period(product: &mut Product, active_till_days: i64) {
let active_till = Utc::now() + Duration::days(active_till_days);
product.set_active_period(active_till);
}
pub fn product_is_purchasable(product: &Product) -> bool {
product.is_purchasable()
}
pub fn product_is_active(product: &Product) -> bool {
product.is_active()
}
pub fn product_cost_in_currency(product: &Product, currency_code: &str) -> Result<Dynamic, Box<EvalAltResult>> {
match product.cost_in_currency(currency_code) {
Some(cost) => Ok(cost.to_rhai()),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to calculate cost in {}", currency_code).into(),
Position::NONE
)))
}
}
pub fn product_cost_in_usd(product: &Product) -> Result<Dynamic, Box<EvalAltResult>> {
match product.cost_in_usd() {
Some(cost) => Ok(cost.to_rhai()),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
"Failed to calculate cost in USD".into(),
Position::NONE
)))
}
}
pub fn product_total_energy_usage(product: &Product) -> f64 {
product.total_energy_usage()
}
pub fn product_components_cost(product: &Product, currency_code: &str) -> Result<Dynamic, Box<EvalAltResult>> {
match product.components_cost(currency_code) {
Some(cost) => Ok(cost.to_rhai()),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to calculate components cost in {}", currency_code).into(),
Position::NONE
)))
}
}
pub fn product_components_cost_in_usd(product: &Product) -> Result<Dynamic, Box<EvalAltResult>> {
match product.components_cost_in_usd() {
Some(cost) => Ok(cost.to_rhai()),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
"Failed to calculate components cost in USD".into(),
Position::NONE
)))
}
}
// ProductBuilder Functions
pub fn product_builder_new() -> ProductBuilder {
ProductBuilder::new()
}
pub fn product_builder_id(builder: ProductBuilder, id: i64) -> ProductBuilder {
builder.id(id as u32)
}
pub fn product_builder_name(builder: ProductBuilder, name: &str) -> ProductBuilder {
builder.name(name)
}
pub fn product_builder_description(builder: ProductBuilder, description: &str) -> ProductBuilder {
builder.description(description)
}
pub fn product_builder_price(builder: ProductBuilder, price: Currency) -> ProductBuilder {
builder.price(price)
}
pub fn product_builder_type(builder: ProductBuilder, type_: ProductType) -> ProductBuilder {
builder.type_(type_)
}
pub fn product_builder_category(builder: ProductBuilder, category: &str) -> ProductBuilder {
builder.category(category)
}
pub fn product_builder_status(builder: ProductBuilder, status: ProductStatus) -> ProductBuilder {
builder.status(status)
}
pub fn product_builder_max_amount(builder: ProductBuilder, max_amount: i64) -> ProductBuilder {
builder.max_amount(max_amount as u16)
}
pub fn product_builder_validity_days(builder: ProductBuilder, validity_days: i64) -> ProductBuilder {
builder.validity_days(validity_days)
}
pub fn product_builder_add_component(builder: ProductBuilder, component: ProductComponent) -> ProductBuilder {
builder.add_component(component)
}
pub fn product_builder_build(builder: ProductBuilder) -> Result<Product, Box<EvalAltResult>> {
builder.build().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
e.into(),
Position::NONE
))
})
}
// Exchange Rate Service Functions
pub fn exchange_rate_convert(amount: f64, from_currency: &str, to_currency: &str) -> Result<f64, Box<EvalAltResult>> {
match EXCHANGE_RATE_SERVICE.convert(amount, from_currency, to_currency) {
Some(converted) => Ok(converted),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to convert {} {} to {}", amount, from_currency, to_currency).into(),
Position::NONE
)))
}
}
pub fn exchange_rate_get_rate(from_currency: &str, to_currency: &str) -> Result<f64, Box<EvalAltResult>> {
match EXCHANGE_RATE_SERVICE.get_rate(from_currency, to_currency) {
Some(rate) => Ok(rate),
None => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get exchange rate from {} to {}", from_currency, to_currency).into(),
Position::NONE
)))
}
}