417 lines
21 KiB
Rust
417 lines
21 KiB
Rust
use rhai::{Engine, Array, Dynamic, ImmutableString, INT, EvalAltResult, NativeCallContext};
|
|
use std::sync::{Arc, Mutex};
|
|
use std::collections::HashMap;
|
|
use std::error::Error as StdError; // For Box<dyn StdError>
|
|
|
|
// Custom error type for Rhai that wraps a String
|
|
#[derive(Debug)]
|
|
struct RhaiStringError(String);
|
|
|
|
impl std::fmt::Display for RhaiStringError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl StdError for RhaiStringError {}
|
|
|
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
use crate::models::finance::account::Account;
|
|
use crate::models::finance::asset::{Asset, AssetType};
|
|
use crate::models::finance::marketplace::{Listing, Bid, ListingStatus, ListingType, BidStatus};
|
|
|
|
// --- Enum to String & String to Enum Helper Functions (domain-specific) ---
|
|
// These remain here as they are specific to the finance models' enums.
|
|
|
|
fn asset_type_to_string(asset_type: &AssetType) -> ImmutableString {
|
|
format!("{:?}", asset_type).into()
|
|
}
|
|
fn string_to_asset_type(s: &str) -> Result<AssetType, Box<EvalAltResult>> {
|
|
match s {
|
|
"Erc20" => Ok(AssetType::Erc20),
|
|
"Erc721" => Ok(AssetType::Erc721),
|
|
"Erc1155" => Ok(AssetType::Erc1155),
|
|
"Native" => Ok(AssetType::Native),
|
|
_ => Err(format!("Invalid AssetType string: {}", s).into()),
|
|
}
|
|
}
|
|
|
|
fn listing_status_to_string(status: &ListingStatus) -> ImmutableString {
|
|
format!("{:?}", status).into()
|
|
}
|
|
fn string_to_listing_status(s: &str) -> Result<ListingStatus, Box<EvalAltResult>> {
|
|
match s.to_lowercase().as_str() {
|
|
"active" => Ok(ListingStatus::Active),
|
|
"sold" => Ok(ListingStatus::Sold),
|
|
"cancelled" => Ok(ListingStatus::Cancelled),
|
|
"expired" => Ok(ListingStatus::Expired),
|
|
_ => Err(format!("Invalid ListingStatus string: {}", s).into()),
|
|
}
|
|
}
|
|
|
|
fn listing_type_to_string(lt: &ListingType) -> ImmutableString {
|
|
format!("{:?}", lt).into()
|
|
}
|
|
fn string_to_listing_type(s: &str) -> Result<ListingType, Box<EvalAltResult>> {
|
|
match s.to_lowercase().as_str() {
|
|
"fixedprice" => Ok(ListingType::FixedPrice),
|
|
"auction" => Ok(ListingType::Auction),
|
|
"exchange" => Ok(ListingType::Exchange),
|
|
_ => Err(format!("Invalid ListingType string: {}", s).into()),
|
|
}
|
|
}
|
|
|
|
fn bid_status_to_string(status: &BidStatus) -> ImmutableString {
|
|
format!("{:?}", status).into()
|
|
}
|
|
fn string_to_bid_status(s: &str) -> Result<BidStatus, Box<EvalAltResult>> {
|
|
match s.to_lowercase().as_str() {
|
|
"active" => Ok(BidStatus::Active),
|
|
"accepted" => Ok(BidStatus::Accepted),
|
|
"rejected" => Ok(BidStatus::Rejected),
|
|
"cancelled" => Ok(BidStatus::Cancelled),
|
|
_ => Err(format!("Invalid BidStatus string: {}", s).into()),
|
|
}
|
|
}
|
|
|
|
pub fn register_rhai_engine_functions(
|
|
engine: &mut Engine,
|
|
db_accounts: Arc<Mutex<HashMap<u32, Account>>>,
|
|
db_assets: Arc<Mutex<HashMap<u32, Asset>>>,
|
|
db_listings: Arc<Mutex<HashMap<u32, Listing>>>,
|
|
) {
|
|
// --- Account model ---
|
|
engine.register_type_with_name::<Account>("Account");
|
|
engine.register_fn("new_account", || -> Account {
|
|
Account::new(None, "", 0, "", "", "", "")
|
|
});
|
|
// Getters
|
|
engine.register_get("id", |acc: &mut Account| acc.base_data.id as INT);
|
|
engine.register_get("created_at_ts", |acc: &mut Account| acc.base_data.created_at);
|
|
engine.register_get("name", |acc: &mut Account| -> ImmutableString { acc.name.clone().into() });
|
|
engine.register_get("user_id", |acc: &mut Account| acc.user_id as INT);
|
|
engine.register_get("description", |acc: &mut Account| -> ImmutableString { acc.description.clone().into() });
|
|
engine.register_get("ledger", |acc: &mut Account| -> ImmutableString { acc.ledger.clone().into() });
|
|
engine.register_get("address", |acc: &mut Account| -> ImmutableString { acc.address.clone().into() });
|
|
engine.register_get("pubkey", |acc: &mut Account| -> ImmutableString { acc.pubkey.clone().into() });
|
|
engine.register_get("assets_list", |acc: &mut Account| -> Result<Array, Box<EvalAltResult>> {
|
|
Ok(acc.assets.iter().cloned().map(rhai::Dynamic::from).collect())
|
|
});
|
|
engine.register_get("modified_at_ts", |acc: &mut Account| acc.base_data.modified_at);
|
|
|
|
// Setters (Builder Pattern)
|
|
engine.register_fn("set_name", |mut acc: Account, name: ImmutableString| -> Result<Account, Box<EvalAltResult>> {
|
|
acc.name = name.to_string(); Ok(acc)
|
|
});
|
|
engine.register_fn("set_user_id", |mut acc: Account, user_id: INT| -> Result<Account, Box<EvalAltResult>> {
|
|
acc.user_id = user_id as u32; Ok(acc)
|
|
});
|
|
engine.register_fn("set_description", |mut acc: Account, description: ImmutableString| -> Result<Account, Box<EvalAltResult>> {
|
|
acc.description = description.to_string(); Ok(acc)
|
|
});
|
|
engine.register_fn("set_ledger", |mut acc: Account, ledger: ImmutableString| -> Result<Account, Box<EvalAltResult>> {
|
|
acc.ledger = ledger.to_string(); Ok(acc)
|
|
});
|
|
engine.register_fn("set_address", |mut acc: Account, address: ImmutableString| -> Result<Account, Box<EvalAltResult>> {
|
|
acc.address = address.to_string(); Ok(acc)
|
|
});
|
|
engine.register_fn("set_pubkey", |mut acc: Account, pubkey: ImmutableString| -> Result<Account, Box<EvalAltResult>> {
|
|
acc.pubkey = pubkey.to_string(); Ok(acc)
|
|
});
|
|
// Action: Add an Asset object to the account's asset list
|
|
engine.register_fn("add_asset", |mut acc: Account, asset: Asset| -> Result<Account, Box<EvalAltResult>> {
|
|
acc.assets.push(asset);
|
|
Ok(acc)
|
|
});
|
|
|
|
// --- Asset model ---
|
|
engine.register_type_with_name::<Asset>("Asset");
|
|
engine.register_fn("new_asset", || -> Asset {
|
|
Asset::new(None, "", "", 0.0, "", AssetType::Native, 0)
|
|
});
|
|
// Getters
|
|
engine.register_get("id", |asset: &mut Asset| asset.base_data.id as INT);
|
|
engine.register_get("created_at_ts", |asset: &mut Asset| asset.base_data.created_at);
|
|
engine.register_get("name", |asset: &mut Asset| -> ImmutableString { asset.name.clone().into() });
|
|
engine.register_get("description", |asset: &mut Asset| -> ImmutableString { asset.description.clone().into() });
|
|
engine.register_get("amount", |asset: &mut Asset| asset.amount);
|
|
engine.register_get("address", |asset: &mut Asset| -> ImmutableString { asset.address.clone().into() });
|
|
engine.register_get("asset_type_str", |asset: &mut Asset| -> ImmutableString { self::asset_type_to_string(&asset.asset_type) });
|
|
engine.register_get("decimals", |asset: &mut Asset| asset.decimals as INT);
|
|
engine.register_get("modified_at_ts", |asset: &mut Asset| asset.base_data.modified_at);
|
|
|
|
// Setters (Builder Pattern)
|
|
engine.register_fn("set_name", |mut asset: Asset, name: ImmutableString| -> Result<Asset, Box<EvalAltResult>> {
|
|
asset.name = name.to_string(); Ok(asset)
|
|
});
|
|
engine.register_fn("set_description", |mut asset: Asset, description: ImmutableString| -> Result<Asset, Box<EvalAltResult>> {
|
|
asset.description = description.to_string(); Ok(asset)
|
|
});
|
|
engine.register_fn("set_amount", |mut asset: Asset, amount: f64| -> Result<Asset, Box<EvalAltResult>> {
|
|
asset.amount = amount; Ok(asset)
|
|
});
|
|
engine.register_fn("set_address", |mut asset: Asset, address: ImmutableString| -> Result<Asset, Box<EvalAltResult>> {
|
|
asset.address = address.to_string(); Ok(asset)
|
|
});
|
|
engine.register_fn("set_asset_type", |mut asset: Asset, asset_type_str: ImmutableString| -> Result<Asset, Box<EvalAltResult>> {
|
|
asset.asset_type = self::string_to_asset_type(asset_type_str.as_str())?;
|
|
Ok(asset)
|
|
});
|
|
engine.register_fn("set_decimals", |mut asset: Asset, decimals: INT| -> Result<Asset, Box<EvalAltResult>> {
|
|
asset.decimals = decimals as u8; Ok(asset)
|
|
});
|
|
|
|
// --- Listing model ---
|
|
engine.register_type_with_name::<Listing>("Listing");
|
|
engine.register_fn("new_listing", || -> Listing {
|
|
Listing::new(None, "", "", "", AssetType::Native, "", 0.0, "", ListingType::FixedPrice, None, Vec::new(), None::<String>)
|
|
});
|
|
// Getters
|
|
engine.register_get("id", |l: &mut Listing| l.base_data.id as INT);
|
|
engine.register_get("created_at_ts", |l: &mut Listing| l.base_data.created_at);
|
|
engine.register_get("modified_at_ts", |l: &mut Listing| l.base_data.modified_at);
|
|
engine.register_get("title", |l: &mut Listing| -> ImmutableString { l.title.clone().into() });
|
|
engine.register_get("description", |l: &mut Listing| -> ImmutableString { l.description.clone().into() });
|
|
engine.register_get("asset_id", |l: &mut Listing| -> ImmutableString { l.asset_id.clone().into() });
|
|
engine.register_get("asset_type_str", |l: &mut Listing| -> ImmutableString { self::asset_type_to_string(&l.asset_type) });
|
|
engine.register_get("seller_id", |l: &mut Listing| -> ImmutableString { l.seller_id.clone().into() });
|
|
engine.register_get("price", |l: &mut Listing| l.price);
|
|
engine.register_get("currency", |l: &mut Listing| -> ImmutableString { l.currency.clone().into() });
|
|
engine.register_get("listing_type", |l: &mut Listing| l.listing_type.clone());
|
|
engine.register_get("status", |l: &mut Listing| l.status.clone());
|
|
engine.register_get("expires_at_ts", |l: &mut Listing| l.expires_at);
|
|
engine.register_get("expires_at_ts_opt", |l: &mut Listing| l.expires_at.map(|dt| dt.timestamp()));
|
|
engine.register_get("tags", |l: &mut Listing| -> Result<Array, Box<EvalAltResult>> {
|
|
Ok(l.tags.iter().map(|s| Dynamic::from(s.clone())).collect())
|
|
});
|
|
engine.register_get("image_url", |l: &mut Listing| -> Option<ImmutableString> { l.image_url.as_ref().map(|s| s.clone().into()) });
|
|
engine.register_get("bids_list", |l: &mut Listing| -> Result<Array, Box<EvalAltResult>> {
|
|
Ok(l.bids.iter().cloned().map(rhai::Dynamic::from).collect())
|
|
});
|
|
// Setters (Builder Pattern)
|
|
engine.register_fn("set_title", |mut l: Listing, title: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.title = title.to_string(); Ok(l)
|
|
});
|
|
engine.register_fn("set_description", |mut l: Listing, description: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.description = description.to_string(); Ok(l)
|
|
});
|
|
engine.register_fn("set_asset_id", |mut l: Listing, asset_id: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.asset_id = asset_id.to_string(); Ok(l)
|
|
});
|
|
engine.register_fn("set_asset_type", |mut l: Listing, asset_type_str: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.asset_type = self::string_to_asset_type(asset_type_str.as_str())?;
|
|
Ok(l)
|
|
});
|
|
engine.register_fn("set_seller_id", |mut l: Listing, seller_id: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.seller_id = seller_id.to_string(); Ok(l)
|
|
});
|
|
engine.register_fn("set_price", |mut l: Listing, price: f64| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.price = price; Ok(l)
|
|
});
|
|
engine.register_fn("set_currency", |mut l: Listing, currency: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.currency = currency.to_string(); Ok(l)
|
|
});
|
|
engine.register_fn("set_listing_type", |mut l: Listing, listing_type: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.listing_type = self::string_to_listing_type(listing_type.as_str())?;
|
|
Ok(l)
|
|
});
|
|
engine.register_fn("set_status_str", |mut l: Listing, status_str: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.status = self::string_to_listing_status(status_str.as_str())?;
|
|
Ok(l)
|
|
});
|
|
engine.register_fn("set_expires_at_ts", |mut l: Listing, expires_at_ts: Option<INT>| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.expires_at = expires_at_ts.map(|ts| DateTime::from_timestamp(ts, 0).unwrap_or_else(|| Utc::now()));
|
|
Ok(l)
|
|
});
|
|
engine.register_fn("set_tags", |mut l: Listing, tags_array: Array| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.tags = tags_array.into_iter().map(|d| d.into_string().unwrap_or_default()).collect();
|
|
Ok(l)
|
|
});
|
|
engine.register_fn("add_tag", |mut l: Listing, tag: ImmutableString| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.tags.push(tag.to_string());
|
|
Ok(l)
|
|
});
|
|
engine.register_fn("set_image_url", |mut l: Listing, image_url: Option<ImmutableString>| -> Result<Listing, Box<EvalAltResult>> {
|
|
l.image_url = image_url.map(|s| s.to_string());
|
|
Ok(l)
|
|
});
|
|
// Listing Action Methods (preserved)
|
|
engine.register_fn("add_listing_bid", |listing: Listing, bid: Bid| -> Result<Listing, Box<EvalAltResult>> {
|
|
listing.add_bid(bid).map_err(|e_str| {
|
|
Box::new(EvalAltResult::ErrorSystem(
|
|
"Failed to add bid".to_string(),
|
|
Box::new(RhaiStringError(e_str.to_string())),
|
|
))
|
|
})
|
|
});
|
|
engine.register_fn("accept_listing_bid", |listing: Listing, bid_index_rhai: i64| -> Result<Listing, Box<EvalAltResult>> {
|
|
let bid_index = bid_index_rhai as usize;
|
|
if bid_index >= listing.bids.len() {
|
|
return Err(Box::new(EvalAltResult::ErrorSystem(
|
|
"Invalid bid index".to_string(),
|
|
Box::new(RhaiStringError(format!("Bid index {} out of bounds for {} bids", bid_index, listing.bids.len()))),
|
|
)));
|
|
}
|
|
|
|
let bid_to_accept = listing.bids[bid_index].clone();
|
|
|
|
if bid_to_accept.status != BidStatus::Active {
|
|
return Err(Box::new(EvalAltResult::ErrorSystem(
|
|
"Bid not active".to_string(),
|
|
Box::new(RhaiStringError(format!("Cannot accept bid at index {} as it is not active (status: {:?})", bid_index, bid_to_accept.status))),
|
|
)));
|
|
}
|
|
|
|
let mut listing_after_sale = listing.complete_sale(bid_to_accept.bidder_id.to_string(), bid_to_accept.amount)
|
|
.map_err(|e_str| Box::new(EvalAltResult::ErrorSystem(
|
|
"Failed to complete sale".to_string(),
|
|
Box::new(RhaiStringError(e_str.to_string())),
|
|
)))?;
|
|
|
|
// Update bid statuses on the new listing state
|
|
for (idx, bid_in_list) in listing_after_sale.bids.iter_mut().enumerate() {
|
|
if idx == bid_index {
|
|
*bid_in_list = bid_in_list.clone().update_status(BidStatus::Accepted);
|
|
} else {
|
|
if bid_in_list.status == BidStatus::Active { // Only reject other active bids
|
|
*bid_in_list = bid_in_list.clone().update_status(BidStatus::Rejected);
|
|
}
|
|
}
|
|
}
|
|
Ok(listing_after_sale)
|
|
});
|
|
engine.register_fn("cancel_listing", |_ctx: NativeCallContext, listing: Listing| -> Result<Listing, Box<EvalAltResult>> {
|
|
listing.cancel().map_err(|e_str| {
|
|
Box::new(EvalAltResult::ErrorSystem(
|
|
"Failed to cancel listing".to_string(),
|
|
Box::new(RhaiStringError(e_str.to_string()))
|
|
))
|
|
})
|
|
});
|
|
// --- Bid model (preserved as is) ---
|
|
engine.register_type_with_name::<Bid>("Bid")
|
|
.register_fn("new_bid",
|
|
|listing_id_rhai: ImmutableString, bidder_id_rhai: INT, amount_rhai: f64, currency_rhai: ImmutableString| -> Bid {
|
|
Bid::new(listing_id_rhai, bidder_id_rhai as u32, amount_rhai, currency_rhai)
|
|
}
|
|
)
|
|
.register_get_set("listing_id",
|
|
|bid: &mut Bid| -> ImmutableString { bid.listing_id.clone().into() },
|
|
|bid: &mut Bid, val: ImmutableString| bid.listing_id = val.to_string()
|
|
)
|
|
.register_get_set("bidder_id", |bid: &mut Bid| bid.bidder_id as INT, |bid: &mut Bid, val: INT| bid.bidder_id = val as u32)
|
|
.register_get_set("amount", |bid: &mut Bid| bid.amount, |bid: &mut Bid, val: f64| bid.amount = val)
|
|
.register_get_set("currency",
|
|
|bid: &mut Bid| -> ImmutableString { bid.currency.clone().into() },
|
|
|bid: &mut Bid, val: ImmutableString| bid.currency = val.to_string()
|
|
)
|
|
.register_get("status_str", |bid: &mut Bid| -> ImmutableString { self::bid_status_to_string(&bid.status) });
|
|
|
|
engine.register_fn("accept_bid_script", |bid: Bid| -> Result<Bid, Box<EvalAltResult>> {
|
|
// Ensure the bid is active before accepting
|
|
if bid.status != BidStatus::Active {
|
|
return Err(Box::new(EvalAltResult::ErrorSystem(
|
|
"Bid not active".to_string(),
|
|
Box::new(RhaiStringError(format!("Cannot accept bid as it is not active (status: {:?})", bid.status)))
|
|
)));
|
|
}
|
|
Ok(bid.update_status(BidStatus::Accepted))
|
|
});
|
|
engine.register_fn("reject_bid_script", |bid: Bid| -> Result<Bid, Box<EvalAltResult>> {
|
|
// Ensure the bid is active before rejecting
|
|
if bid.status != BidStatus::Active {
|
|
return Err(Box::new(EvalAltResult::ErrorSystem(
|
|
"Bid not active".to_string(),
|
|
Box::new(RhaiStringError(format!("Cannot reject bid as it is not active (status: {:?})", bid.status)))
|
|
)));
|
|
}
|
|
Ok(bid.update_status(BidStatus::Rejected))
|
|
});
|
|
engine.register_fn("cancel_bid_script", |bid: Bid| -> Result<Bid, Box<EvalAltResult>> {
|
|
// Ensure the bid is active before cancelling
|
|
if bid.status != BidStatus::Active {
|
|
return Err(Box::new(EvalAltResult::ErrorSystem(
|
|
"Bid not active".to_string(),
|
|
Box::new(RhaiStringError(format!("Cannot cancel bid as it is not active (status: {:?})", bid.status)))
|
|
)));
|
|
}
|
|
Ok(bid.update_status(BidStatus::Cancelled))
|
|
});
|
|
|
|
// --- Global Helper Functions (Enum conversions, potentially already covered by macros but good for direct script use) ---
|
|
engine.register_fn("str_to_asset_type", |s: ImmutableString| self::string_to_asset_type(s.as_str()));
|
|
engine.register_fn("asset_type_to_str", self::asset_type_to_string);
|
|
engine.register_fn("str_to_listing_status", |s: ImmutableString| self::string_to_listing_status(s.as_str()));
|
|
engine.register_fn("listing_status_to_str", self::listing_status_to_string);
|
|
engine.register_fn("str_to_listing_type", |s: ImmutableString| self::string_to_listing_type(s.as_str()));
|
|
engine.register_fn("listing_type_to_str", self::listing_type_to_string);
|
|
engine.register_fn("str_to_bid_status", |s: ImmutableString| self::string_to_bid_status(s.as_str()));
|
|
engine.register_fn("bid_status_to_str", self::bid_status_to_string);
|
|
|
|
// --- Mock DB functions (preserved) ---
|
|
let accounts_db_clone = Arc::clone(&db_accounts);
|
|
engine.register_fn("set_account", move |mut account: Account| -> Account {
|
|
let mut db = accounts_db_clone.lock().unwrap();
|
|
if account.base_data.id == 0 {
|
|
let next_id = db.keys().max().cloned().unwrap_or(0) + 1;
|
|
account.base_data.update_id(next_id);
|
|
}
|
|
db.insert(account.base_data.id, account.clone());
|
|
account
|
|
});
|
|
|
|
let accounts_db_clone_get = Arc::clone(&db_accounts);
|
|
engine.register_fn("get_account_by_id", move |id_rhai: INT| -> Result<Account, Box<EvalAltResult>> {
|
|
let db = accounts_db_clone_get.lock().unwrap();
|
|
match db.get(&(id_rhai as u32)) {
|
|
Some(account) => Ok(account.clone()),
|
|
None => Err(format!("Account not found with ID: {}", id_rhai).into()),
|
|
}
|
|
});
|
|
|
|
let assets_db_clone = Arc::clone(&db_assets);
|
|
engine.register_fn("set_asset", move |mut asset: Asset| -> Asset {
|
|
let mut db = assets_db_clone.lock().unwrap();
|
|
if asset.base_data.id == 0 {
|
|
let next_id = db.keys().max().cloned().unwrap_or(0) + 1;
|
|
asset.base_data.update_id(next_id);
|
|
}
|
|
db.insert(asset.base_data.id, asset.clone());
|
|
asset
|
|
});
|
|
|
|
let assets_db_clone_get = Arc::clone(&db_assets);
|
|
engine.register_fn("get_asset_by_id", move |id_rhai: INT| -> Result<Asset, Box<EvalAltResult>> {
|
|
let db = assets_db_clone_get.lock().unwrap();
|
|
match db.get(&(id_rhai as u32)) {
|
|
Some(asset) => Ok(asset.clone()),
|
|
None => Err(format!("Asset not found with ID: {}", id_rhai).into()),
|
|
}
|
|
});
|
|
|
|
let listings_db_clone = Arc::clone(&db_listings);
|
|
engine.register_fn("set_listing", move |mut listing: Listing| -> Listing {
|
|
let mut db = listings_db_clone.lock().unwrap();
|
|
if listing.base_data.id == 0 {
|
|
let next_id = db.keys().max().cloned().unwrap_or(0) + 1;
|
|
listing.base_data.update_id(next_id);
|
|
}
|
|
db.insert(listing.base_data.id, listing.clone());
|
|
listing
|
|
});
|
|
|
|
let listings_db_clone_get = Arc::clone(&db_listings);
|
|
engine.register_fn("get_listing_by_id", move |id_rhai: INT| -> Result<Listing, Box<EvalAltResult>> {
|
|
let db = listings_db_clone_get.lock().unwrap();
|
|
match db.get(&(id_rhai as u32)) {
|
|
Some(listing) => Ok(listing.clone()),
|
|
None => Err(format!("Listing not found with ID: {}", id_rhai).into()),
|
|
}
|
|
});
|
|
|
|
// Global timestamp function for scripts to get current time
|
|
engine.register_fn("timestamp", || Utc::now().timestamp());
|
|
}
|