improve and add models

This commit is contained in:
timurgordon 2025-06-12 05:22:17 +03:00
parent c3f6b91aa0
commit 00c4e6a1eb
19 changed files with 1520 additions and 42 deletions

View File

@ -81,3 +81,8 @@ required-features = ["rhai"]
name = "biz_rhai"
path = "examples/biz_rhai/example.rs"
required-features = ["rhai"]
[[example]]
name = "library_rhai"
path = "examples/library_rhai/example.rs"
required-features = ["rhai"]

View File

@ -0,0 +1,38 @@
use heromodels::db::hero::OurDB;
use heromodels::models::register_library_rhai_module;
use rhai::Engine;
use std::sync::Arc;
use std::{fs, path::Path};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_library_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_library_rhai_module(&mut engine, db.clone());
// Load and evaluate the Rhai script
let script_path = Path::new(file!()).parent().unwrap().join("library.rhai");
let script = fs::read_to_string(&script_path)?;
println!("--- Running Library Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,78 @@
// heromodels/examples/library_rhai/library.rhai
print("--- Testing Library Rhai Module ---");
// --- Image ---
print("\n1. Creating and saving an image...");
let my_image = new_image()
.title("A Beautiful Sunset")
.description("A photo taken from a drone.")
.url("https://example.com/sunset.jpg")
.width(1920)
.height(1080);
let saved_image = save_image(my_image);
print(" > Saved image with ID: " + saved_image.id);
let image_id = saved_image.id;
// --- PDF ---
print("\n2. Creating and saving a PDF...");
let my_pdf = new_pdf()
.title("Rust Programming Guide")
.description("A comprehensive guide to Rust.")
.url("https://example.com/rust.pdf")
.page_count(500);
let saved_pdf = save_pdf(my_pdf);
print(" > Saved PDF with ID: " + saved_pdf.id);
let pdf_id = saved_pdf.id;
// --- Markdown ---
print("\n3. Creating and saving a Markdown document...");
let my_markdown = new_markdown()
.title("Meeting Notes")
.description("Notes from the weekly sync.")
.content("# Meeting Notes\n\n- Discussed project status.\n- Planned next sprint.");
let saved_markdown = save_markdown(my_markdown);
print(" > Saved Markdown with ID: " + saved_markdown.id);
let markdown_id = saved_markdown.id;
// --- Collection ---
print("\n4. Creating a collection and adding items...");
let my_collection = new_collection()
.title("My Awesome Collection")
.description("A collection of various media.")
.add_image(image_id)
.add_pdf(pdf_id)
.add_markdown(markdown_id);
let saved_collection = save_collection(my_collection);
print(" > Saved collection with ID: " + saved_collection.id);
let collection_id = saved_collection.id;
// --- Verification ---
print("\n5. Verifying saved data...");
let fetched_collection = get_collection(collection_id);
print(" > Fetched collection: '" + fetched_collection.title + "'");
print(" > Collection contains " + fetched_collection.images + " image(s).");
print(" > Collection contains " + fetched_collection.pdfs + " pdf(s).");
print(" > Collection contains " + fetched_collection.markdowns + " markdown(s).");
let fetched_image = get_image(image_id);
print(" > Fetched image title: '" + fetched_image.title + "'");
if (fetched_image.url != "https://example.com/sunset.jpg") {
throw "Image URL mismatch!";
}
print(" > Image URL verified.");
// --- Deletion ---
print("\n6. Cleaning up database...");
delete_image(image_id);
print(" > Deleted image with ID: " + image_id);
delete_pdf(pdf_id);
print(" > Deleted PDF with ID: " + pdf_id);
delete_markdown(markdown_id);
print(" > Deleted Markdown with ID: " + markdown_id);
delete_collection(collection_id);
print(" > Deleted collection with ID: " + collection_id);

View File

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use heromodels_core::{BaseModelData, Model, IndexKey, IndexKeyBuilder, Index};
use heromodels_core::{BaseModelData, Index};
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
use heromodels_core::BaseModelDataOps;
use heromodels_derive::model;

View File

@ -1,7 +1,5 @@
use serde::{Serialize, Deserialize};
use heromodels_core::BaseModelData;
use heromodels_core::Model;
use heromodels_core::BaseModelDataOps;
use heromodels_derive::model;
// ProductType represents the type of a product

View File

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use heromodels_core::{BaseModelData, Model, BaseModelDataOps};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]

View File

@ -1,4 +1,3 @@
use chrono::{DateTime, Utc};
use heromodels_core::BaseModelData;
use heromodels_derive::model;
// Temporarily removed to fix compilation issues

View File

@ -2,7 +2,6 @@ use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use chrono::{DateTime, Utc};
use crate::db::Db;
use super::calendar::{Event, Attendee, Calendar, AttendanceStatus};
@ -22,16 +21,6 @@ fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
)
}
// Helper to convert i64 from Rhai to u64 for timestamps or other large numbers
fn val_from_i64_to_u64(val_i64: i64) -> Result<u64, Box<EvalAltResult>> {
u64::try_from(val_i64).map_err(|_|
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert value '{}' to u64", val_i64).into(),
Position::NONE
))
)
}
#[export_module]
mod rhai_calendar_module {
// --- Event Functions ---

View File

@ -0,0 +1,86 @@
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 serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Represents an event in a calendar
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
pub struct Circle {
/// Base model data
pub base_data: BaseModelData,
#[index]
pub title: String,
pub ws_url: String,
/// Optional description of the circle
pub description: Option<String>,
/// List of related circles
pub circles: Vec<String>,
/// Logo URL or symbol for the circle
pub logo: Option<String>,
/// Theme settings for the circle (colors, styling, etc.)
pub theme: HashMap<String, String>,
}
impl Circle {
/// Creates a new circle
pub fn new() -> Self {
Self {
base_data: BaseModelData::new(),
title: String::new(),
ws_url: String::new(),
description: None,
circles: Vec::new(),
logo: None,
theme: HashMap::new(),
}
}
/// Sets the title for the circle
pub fn title(mut self, title: impl ToString) -> Self {
self.title = title.to_string();
self
}
/// Sets the ws_url for the circle
pub fn ws_url(mut self, ws_url: impl ToString) -> Self {
self.ws_url = ws_url.to_string();
self
}
/// Sets the description for the circle
pub fn description(mut self, description: impl ToString) -> Self {
self.description = Some(description.to_string());
self
}
/// Sets the logo for the circle
pub fn logo(mut self, logo: impl ToString) -> Self {
self.logo = Some(logo.to_string());
self
}
/// Sets a theme property for the circle
pub fn theme_property(mut self, key: impl ToString, value: impl ToString) -> Self {
self.theme.insert(key.to_string(), value.to_string());
self
}
/// Sets the entire theme for the circle
pub fn theme(mut self, theme: HashMap<String, String>) -> Self {
self.theme = theme;
self
}
/// Adds a related circle
pub fn add_circle(mut self, circle: String) -> Self {
// Prevent duplicate circles
if !self.circles.iter().any(|a| *a == circle) {
self.circles.push(circle);
}
self
}
}

View File

@ -0,0 +1,7 @@
// Export calendar module
pub mod circle;
pub mod rhai;
// Re-export Calendar, Event, Attendee, and AttendanceStatus from the inner calendar module (calendar.rs) within src/models/calendar/mod.rs
pub use self::circle::{Circle};
pub use rhai::register_circle_rhai_module;

View File

@ -0,0 +1,201 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array, CustomType};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use super::circle::{Circle};
type RhaiCircle = Circle;
use crate::db::hero::OurDB;
use crate::db::Collection;
use serde::Serialize;
use std::collections::HashMap;
use serde_json;
/// Registers a `.json()` method for any type `T` that implements the required traits.
fn register_json_method<T>(engine: &mut Engine)
where
// The type must be:
T: CustomType + Clone + Serialize, // A clonable, serializable, custom type for Rhai
{
// This is the function that will be called when a script runs '.json()'
let to_json_fn = |obj: &mut T| -> Result<String, Box<EvalAltResult>> {
// Use serde_json to serialize the object to a pretty-formatted string.
// The '?' will automatically convert any serialization error into a Rhai error.
serde_json::to_string(obj).map_err(|e| e.to_string().into())
};
// Register the function as a method named "json" for the type 'T'.
engine.build_type::<T>().register_fn("json", to_json_fn);
}
// 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_circle_module {
// --- Circle Functions ---
#[rhai_fn(name = "new_circle")]
pub fn new_circle() -> RhaiCircle {
Circle::new()
}
/// Sets the circle title
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn circle_title(circle: &mut RhaiCircle, title: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.title(title);
Ok(circle.clone())
}
/// Sets the circle ws_url
#[rhai_fn(name = "ws_url", return_raw, global, pure)]
pub fn circle_ws_url(circle: &mut RhaiCircle, ws_url: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.ws_url(ws_url);
Ok(circle.clone())
}
/// Sets the circle description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn circle_description(circle: &mut RhaiCircle, description: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.description(description);
Ok(circle.clone())
}
/// Sets the circle logo
#[rhai_fn(name = "logo", return_raw, global, pure)]
pub fn circle_logo(circle: &mut RhaiCircle, logo: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.logo(logo);
Ok(circle.clone())
}
/// Sets the circle theme
#[rhai_fn(name = "theme", return_raw, global, pure)]
pub fn circle_theme(circle: &mut RhaiCircle, theme: HashMap<String, String>) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.theme(theme);
Ok(circle.clone())
}
/// Adds an attendee to the circle
#[rhai_fn(name = "add_circle", return_raw, global, pure)]
pub fn circle_add_circle(circle: &mut RhaiCircle, added_circle: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
// Use take to get ownership of the circle
let owned_circle = mem::take(circle);
*circle = owned_circle.add_circle(added_circle);
Ok(circle.clone())
}
// Circle Getters
#[rhai_fn(get = "id", pure)]
pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 { circle.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 { circle.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 { circle.base_data.modified_at }
#[rhai_fn(get = "title", pure)]
pub fn get_circle_title(circle: &mut RhaiCircle) -> String { circle.title.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_circle_description(circle: &mut RhaiCircle) -> Option<String> { circle.description.clone() }
#[rhai_fn(get = "circles", pure)]
pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec<String> { circle.circles.clone() }
#[rhai_fn(get = "ws_url", pure)]
pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String { circle.ws_url.clone() }
#[rhai_fn(get = "logo", pure)]
pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option<String> { circle.logo.clone() }
#[rhai_fn(get = "theme", pure)]
pub fn get_circle_theme(circle: &mut RhaiCircle) -> HashMap<String, String> { circle.theme.clone() }
}
pub fn register_circle_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_circle_module);
engine.register_global_module(module.into());
// Create a module for database functions
let mut db_module = Module::new();
// Manually register database functions as they need to capture 'db'
let db_clone_set_circle = db.clone();
db_module.set_native_fn("save_circle", move |circle: Circle| -> Result<Circle, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_circle.set(&circle)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_circle: {}", e).into(), Position::NONE)))?;
// Return the updated circle with the correct ID
Ok(result.1)
});
register_json_method::<Circle>(engine);
// Manually register database functions as they need to capture 'db'
let db_clone_delete_circle = db.clone();
db_module.set_native_fn("delete_circle", move |circle: Circle| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_circle.collection::<Circle>()
.expect("can open circle collection")
.delete_by_id(circle.base_data.id)
.expect("can delete circle");
// Return the updated circle with the correct ID
Ok(result)
});
let db_clone_get_circle = db.clone();
db_module.set_native_fn("get_circle", move || -> Result<Circle, Box<EvalAltResult>> {
// Use the Collection trait method directly
let all_circles: Vec<Circle> = db_clone_get_circle.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_circle: {}", e).into(), Position::NONE)))?;
if let Some(first_circle) = all_circles.first() {
Ok(first_circle.clone())
} else {
Err(Box::new(EvalAltResult::ErrorRuntime("Circle not found".into(), Position::NONE)))
}
});
let db_clone_get_circle_by_id = db.clone();
db_module.set_native_fn("get_circle_by_id", move |id_i64: INT| -> Result<Circle, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_circle_by_id.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_circle_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Circle with ID {} not found", id_u32).into(), Position::NONE)))
});
// Add list_circles function to get all circles
let db_clone_list_circles = db.clone();
db_module.set_native_fn("list_circles", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_circles.collection::<Circle>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get circle collection: {:?}", e).into(),
Position::NONE
)))?;
let circles = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all circles: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for circle in circles {
array.push(Dynamic::from(circle));
}
Ok(Dynamic::from(array))
});
// Register the database module globally
engine.register_global_module(db_module.into());
println!("Successfully registered circle Rhai module using export_module approach.");
}

View File

@ -80,13 +80,13 @@ impl Account {
/// Get the total value of all assets in the account
pub fn total_value(&self) -> f64 {
/// TODO: implement
// TODO: implement
0.0
}
/// Find an asset by name
pub fn find_asset_by_name(&self, name: &str) -> Option<&Asset> {
/// TODO: implement
pub fn find_asset_by_name(&self, _name: &str) -> Option<&Asset> {
// TODO: implement
return None
}
}

View File

@ -2,7 +2,7 @@ use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use chrono::{DateTime, Utc};
use chrono::Utc;
use super::account::Account;
use super::asset::{Asset, AssetType};
@ -10,7 +10,6 @@ use super::marketplace::{Listing, Bid, ListingStatus, ListingType, BidStatus};
use crate::db::hero::OurDB;
use crate::db::{Collection, Db};
use heromodels_core::Model;
type RhaiAccount = Account;
type RhaiAsset = Asset;
@ -689,18 +688,8 @@ mod bid_module {
Ok(bid.clone())
}
}
use std::collections::HashMap;
use std::sync::Mutex;
use rhai::ImmutableString;
// Custom error type for Rhai
struct RhaiStringError(String);
impl std::fmt::Debug for RhaiStringError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
// 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 ---

View File

@ -12,7 +12,6 @@ type RhaiSignatureRequirement = SignatureRequirement;
use crate::db::hero::OurDB;
use crate::db::Collection;
use crate::db::Db;
use heromodels_core::Model;
// Helper to convert i64 from Rhai to u32 for IDs
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
@ -24,16 +23,6 @@ fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
)
}
// Helper to convert i64 from Rhai to u64 for timestamps or other large numbers
fn val_from_i64_to_u64(val_i64: i64) -> Result<u64, Box<EvalAltResult>> {
u64::try_from(val_i64).map_err(|_|
Box::new(EvalAltResult::ErrorArithmetic(
format!("Failed to convert value '{}' to u64", val_i64).into(),
Position::NONE
))
)
}
#[export_module]
mod rhai_flow_module {
// --- Flow Functions ---

View File

@ -0,0 +1,91 @@
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use rhai::{CustomType, TypeBuilder};
/// Represents a collection of library items.
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Collection {
/// Base model data
pub base_data: BaseModelData,
/// Title of the collection
#[index]
pub title: String,
/// Optional description of the collection
pub description: Option<String>,
/// List of image item IDs belonging to this collection
pub images: Vec<u32>,
/// List of PDF item IDs belonging to this collection
pub pdfs: Vec<u32>,
/// List of Markdown item IDs belonging to this collection
pub markdowns: Vec<u32>,
/// List of Book item IDs belonging to this collection
pub books: Vec<u32>,
/// List of Slides item IDs belonging to this collection
pub slides: Vec<u32>,
}
impl Default for Collection {
fn default() -> Self {
Self {
base_data: BaseModelData::new(),
title: String::new(),
description: None,
images: Vec::new(),
pdfs: Vec::new(),
markdowns: Vec::new(),
books: Vec::new(),
slides: Vec::new(),
}
}
}
impl Collection {
/// Creates a new `Collection` with default values.
pub fn new() -> Self {
Self::default()
}
/// Sets the title of the collection.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
/// Sets the description of the collection.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
/// Adds an image ID to the collection.
pub fn add_image(mut self, image_id: u32) -> Self {
self.images.push(image_id);
self
}
/// Adds a PDF ID to the collection.
pub fn add_pdf(mut self, pdf_id: u32) -> Self {
self.pdfs.push(pdf_id);
self
}
/// Adds a markdown ID to the collection.
pub fn add_markdown(mut self, markdown_id: u32) -> Self {
self.markdowns.push(markdown_id);
self
}
/// Adds a book ID to the collection.
pub fn add_book(mut self, book_id: u32) -> Self {
self.books.push(book_id);
self
}
/// Adds a slides ID to the collection.
pub fn add_slides(mut self, slides_id: u32) -> Self {
self.slides.push(slides_id);
self
}
}

View File

@ -0,0 +1,369 @@
use heromodels_core::BaseModelData;
use heromodels_derive::model;
use rhai::{CustomType, TypeBuilder};
use serde::{Deserialize, Serialize};
/// Represents an Image library item.
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Image {
/// Base model data
pub base_data: BaseModelData,
/// Title of the image
#[index]
pub title: String,
/// Optional description of the image
pub description: Option<String>,
/// URL of the image
pub url: String,
/// Width of the image in pixels
pub width: u32,
/// Height of the image in pixels
pub height: u32,
}
impl Default for Image {
fn default() -> Self {
Self {
base_data: BaseModelData::new(),
title: String::new(),
description: None,
url: String::new(),
width: 0,
height: 0,
}
}
}
impl Image {
/// Creates a new `Image` with default values.
pub fn new() -> Self {
Self::default()
}
/// Sets the title of the image.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
/// Sets the description of the image.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
/// Sets the URL of the image.
pub fn url(mut self, url: impl Into<String>) -> Self {
self.url = url.into();
self
}
/// Sets the width of the image.
pub fn width(mut self, width: u32) -> Self {
self.width = width;
self
}
/// Sets the height of the image.
pub fn height(mut self, height: u32) -> Self {
self.height = height;
self
}
}
/// Represents a PDF document library item.
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Pdf {
/// Base model data
pub base_data: BaseModelData,
/// Title of the PDF
#[index]
pub title: String,
/// Optional description of the PDF
pub description: Option<String>,
/// URL of the PDF file
pub url: String,
/// Number of pages in the PDF
pub page_count: u32,
}
impl Default for Pdf {
fn default() -> Self {
Self {
base_data: BaseModelData::new(),
title: String::new(),
description: None,
url: String::new(),
page_count: 0,
}
}
}
impl Pdf {
/// Creates a new `Pdf` with default values.
pub fn new() -> Self {
Self::default()
}
/// Sets the title of the PDF.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
/// Sets the description of the PDF.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
/// Sets the URL of the PDF.
pub fn url(mut self, url: impl Into<String>) -> Self {
self.url = url.into();
self
}
/// Sets the page count of the PDF.
pub fn page_count(mut self, page_count: u32) -> Self {
self.page_count = page_count;
self
}
}
/// Represents a Markdown document library item.
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Markdown {
/// Base model data
pub base_data: BaseModelData,
/// Title of the document
#[index]
pub title: String,
/// Optional description of the document
pub description: Option<String>,
/// The markdown content
pub content: String,
}
impl Default for Markdown {
fn default() -> Self {
Self {
base_data: BaseModelData::new(),
title: String::new(),
description: None,
content: String::new(),
}
}
}
impl Markdown {
/// Creates a new `Markdown` document with default values.
pub fn new() -> Self {
Self::default()
}
/// Sets the title of the document.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
/// Sets the description of the document.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
/// Sets the content of the document.
pub fn content(mut self, content: impl Into<String>) -> Self {
self.content = content.into();
self
}
}
/// Represents a table of contents entry for a book.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct TocEntry {
/// Title of the chapter/section
pub title: String,
/// Page number (index in the pages array)
pub page: u32,
/// Optional subsections
pub subsections: Vec<TocEntry>,
}
impl Default for TocEntry {
fn default() -> Self {
Self {
title: String::new(),
page: 0,
subsections: Vec::new(),
}
}
}
impl TocEntry {
/// Creates a new `TocEntry` with default values.
pub fn new() -> Self {
Self::default()
}
/// Sets the title of the TOC entry.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
/// Sets the page number of the TOC entry.
pub fn page(mut self, page: u32) -> Self {
self.page = page;
self
}
/// Adds a subsection to the TOC entry.
pub fn add_subsection(mut self, subsection: TocEntry) -> Self {
self.subsections.push(subsection);
self
}
}
/// Represents a Book library item (collection of markdown pages with TOC).
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Book {
/// Base model data
pub base_data: BaseModelData,
/// Title of the book
#[index]
pub title: String,
/// Optional description of the book
pub description: Option<String>,
/// Table of contents
pub table_of_contents: Vec<TocEntry>,
/// Pages content (markdown strings)
pub pages: Vec<String>,
}
impl Default for Book {
fn default() -> Self {
Self {
base_data: BaseModelData::new(),
title: String::new(),
description: None,
table_of_contents: Vec::new(),
pages: Vec::new(),
}
}
}
impl Book {
/// Creates a new `Book` with default values.
pub fn new() -> Self {
Self::default()
}
/// Sets the title of the book.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
/// Sets the description of the book.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
/// Adds a page to the book.
pub fn add_page(mut self, content: impl Into<String>) -> Self {
self.pages.push(content.into());
self
}
/// Adds a TOC entry to the book.
pub fn add_toc_entry(mut self, entry: TocEntry) -> Self {
self.table_of_contents.push(entry);
self
}
/// Sets the table of contents.
pub fn table_of_contents(mut self, toc: Vec<TocEntry>) -> Self {
self.table_of_contents = toc;
self
}
/// Sets all pages at once.
pub fn pages(mut self, pages: Vec<String>) -> Self {
self.pages = pages;
self
}
}
/// Represents a Slides library item (collection of images for slideshow).
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Slides {
/// Base model data
pub base_data: BaseModelData,
/// Title of the slideshow
#[index]
pub title: String,
/// Optional description of the slideshow
pub description: Option<String>,
/// List of slide image URLs
pub slide_urls: Vec<String>,
/// Optional slide titles/captions
pub slide_titles: Vec<Option<String>>,
}
impl Default for Slides {
fn default() -> Self {
Self {
base_data: BaseModelData::new(),
title: String::new(),
description: None,
slide_urls: Vec::new(),
slide_titles: Vec::new(),
}
}
}
impl Slides {
/// Creates a new `Slides` with default values.
pub fn new() -> Self {
Self::default()
}
/// Sets the title of the slideshow.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
/// Sets the description of the slideshow.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
/// Adds a slide with URL and optional title.
pub fn add_slide(mut self, url: impl Into<String>, title: Option<String>) -> Self {
self.slide_urls.push(url.into());
self.slide_titles.push(title);
self
}
/// Sets all slide URLs at once.
pub fn slide_urls(mut self, urls: Vec<String>) -> Self {
self.slide_urls = urls;
self
}
/// Sets all slide titles at once.
pub fn slide_titles(mut self, titles: Vec<Option<String>>) -> Self {
self.slide_titles = titles;
self
}
}

View File

@ -0,0 +1,4 @@
pub mod collection;
pub mod items;
pub mod rhai;
pub use rhai::register_library_rhai_module;

View File

@ -0,0 +1,626 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, CustomType, TypeBuilder, Position, Module, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use serde::Serialize;
use serde_json;
use super::collection::{Collection as RhaiCollection};
use super::items::{Image as RhaiImage, Pdf as RhaiPdf, Markdown as RhaiMarkdown, Book as RhaiBook, Slides as RhaiSlides, TocEntry as RhaiTocEntry};
use crate::db::hero::OurDB;
use crate::db::Collection as DbCollectionTrait;
// 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::ErrorMismatchDataType(
"u32".to_string(), // Expected type
format!("i64 value ({}) that cannot be represented as u32", id_i64), // Actual type/value description
Position::NONE
))
)
}
/// Registers a `.json()` method for any type `T` that implements the required traits.
fn register_json_method<T>(engine: &mut Engine)
where
// The type must be:
T: CustomType + Clone + Serialize, // A clonable, serializable, custom type for Rhai
{
// This is the function that will be called when a script runs '.json()'
let to_json_fn = |obj: &mut T| -> Result<String, Box<EvalAltResult>> {
// Use serde_json to serialize the object to a pretty-formatted string.
// The '?' will automatically convert any serialization error into a Rhai error.
serde_json::to_string(obj).map_err(|e| e.to_string().into())
};
// Register the function as a method named "json" for the type 'T'.
engine.build_type::<T>().register_fn("json", to_json_fn);
}
// Wrapper type for a list of collections to enable .json() method via register_json_method
#[derive(Debug, Clone, Serialize, CustomType)]
#[rhai_type(name = "CollectionArray")]
pub struct RhaiCollectionArray(pub Vec<RhaiCollection>);
#[export_module]
mod rhai_library_module {
// --- Collection Functions ---
#[rhai_fn(name = "new_collection")]
pub fn new_collection() -> RhaiCollection {
RhaiCollection::new()
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn collection_title(collection: &mut RhaiCollection, title: String) -> Result<RhaiCollection, Box<EvalAltResult>> {
let owned = mem::take(collection);
*collection = owned.title(title);
Ok(collection.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn collection_description(collection: &mut RhaiCollection, description: String) -> Result<RhaiCollection, Box<EvalAltResult>> {
let owned = mem::take(collection);
*collection = owned.description(description);
Ok(collection.clone())
}
#[rhai_fn(name = "add_image", return_raw, global, pure)]
pub fn collection_add_image(collection: &mut RhaiCollection, image_id: i64) -> Result<RhaiCollection, Box<EvalAltResult>> {
let id = id_from_i64_to_u32(image_id)?;
let owned = mem::take(collection);
*collection = owned.add_image(id);
Ok(collection.clone())
}
#[rhai_fn(name = "add_pdf", return_raw, global, pure)]
pub fn collection_add_pdf(collection: &mut RhaiCollection, pdf_id: i64) -> Result<RhaiCollection, Box<EvalAltResult>> {
let id = id_from_i64_to_u32(pdf_id)?;
let owned = mem::take(collection);
*collection = owned.add_pdf(id);
Ok(collection.clone())
}
#[rhai_fn(name = "add_markdown", return_raw, global, pure)]
pub fn collection_add_markdown(collection: &mut RhaiCollection, markdown_id: i64) -> Result<RhaiCollection, Box<EvalAltResult>> {
let id = id_from_i64_to_u32(markdown_id)?;
let owned = mem::take(collection);
*collection = owned.add_markdown(id);
Ok(collection.clone())
}
#[rhai_fn(name = "add_book", return_raw, global, pure)]
pub fn collection_add_book(collection: &mut RhaiCollection, book_id: i64) -> Result<RhaiCollection, Box<EvalAltResult>> {
let id = id_from_i64_to_u32(book_id)?;
let owned = mem::take(collection);
*collection = owned.add_book(id);
Ok(collection.clone())
}
#[rhai_fn(name = "add_slides", return_raw, global, pure)]
pub fn collection_add_slides(collection: &mut RhaiCollection, slides_id: i64) -> Result<RhaiCollection, Box<EvalAltResult>> {
let id = id_from_i64_to_u32(slides_id)?;
let owned = mem::take(collection);
*collection = owned.add_slides(id);
Ok(collection.clone())
}
#[rhai_fn(get = "id", pure)]
pub fn get_collection_id(collection: &mut RhaiCollection) -> i64 { collection.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_collection_created_at(collection: &mut RhaiCollection) -> i64 { collection.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_collection_modified_at(collection: &mut RhaiCollection) -> i64 { collection.base_data.modified_at }
#[rhai_fn(get = "title", pure)]
pub fn get_collection_title(collection: &mut RhaiCollection) -> String { collection.title.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_collection_description(collection: &mut RhaiCollection) -> Option<String> { collection.description.clone() }
#[rhai_fn(get = "images", pure)]
pub fn get_collection_images(collection: &mut RhaiCollection) -> Vec<i64> { collection.images.clone().into_iter().map(|id| id as i64).collect() }
#[rhai_fn(get = "pdfs", pure)]
pub fn get_collection_pdfs(collection: &mut RhaiCollection) -> Vec<i64> { collection.pdfs.clone().into_iter().map(|id| id as i64).collect() }
#[rhai_fn(get = "markdowns", pure)]
pub fn get_collection_markdowns(collection: &mut RhaiCollection) -> Vec<i64> { collection.markdowns.clone().into_iter().map(|id| id as i64).collect() }
#[rhai_fn(get = "books", pure)]
pub fn get_collection_books(collection: &mut RhaiCollection) -> Vec<i64> { collection.books.clone().into_iter().map(|id| id as i64).collect() }
#[rhai_fn(get = "slides", pure)]
pub fn get_collection_slides(collection: &mut RhaiCollection) -> Vec<i64> { collection.slides.clone().into_iter().map(|id| id as i64).collect() }
// --- Image Functions ---
#[rhai_fn(name = "new_image")]
pub fn new_image() -> RhaiImage {
RhaiImage::new()
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn image_title(image: &mut RhaiImage, title: String) -> Result<RhaiImage, Box<EvalAltResult>> {
let owned = mem::take(image);
*image = owned.title(title);
Ok(image.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn image_description(image: &mut RhaiImage, description: String) -> Result<RhaiImage, Box<EvalAltResult>> {
let owned = mem::take(image);
*image = owned.description(description);
Ok(image.clone())
}
#[rhai_fn(name = "url", return_raw, global, pure)]
pub fn image_url(image: &mut RhaiImage, url: String) -> Result<RhaiImage, Box<EvalAltResult>> {
let owned = mem::take(image);
*image = owned.url(url);
Ok(image.clone())
}
#[rhai_fn(name = "width", return_raw, global, pure)]
pub fn image_width(image: &mut RhaiImage, width: i64) -> Result<RhaiImage, Box<EvalAltResult>> {
let owned = mem::take(image);
*image = owned.width(width as u32);
Ok(image.clone())
}
#[rhai_fn(name = "height", return_raw, global, pure)]
pub fn image_height(image: &mut RhaiImage, height: i64) -> Result<RhaiImage, Box<EvalAltResult>> {
let owned = mem::take(image);
*image = owned.height(height as u32);
Ok(image.clone())
}
#[rhai_fn(get = "id", pure)]
pub fn get_image_id(image: &mut RhaiImage) -> i64 { image.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_image_created_at(image: &mut RhaiImage) -> i64 { image.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_image_modified_at(image: &mut RhaiImage) -> i64 { image.base_data.modified_at }
#[rhai_fn(get = "title", pure)]
pub fn get_image_title(image: &mut RhaiImage) -> String { image.title.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_image_description(image: &mut RhaiImage) -> Option<String> { image.description.clone() }
#[rhai_fn(get = "url", pure)]
pub fn get_image_url(image: &mut RhaiImage) -> String { image.url.clone() }
#[rhai_fn(get = "width", pure)]
pub fn get_image_width(image: &mut RhaiImage) -> u32 { image.width }
#[rhai_fn(get = "height", pure)]
pub fn get_image_height(image: &mut RhaiImage) -> u32 { image.height }
// --- Pdf Functions ---
#[rhai_fn(name = "new_pdf")]
pub fn new_pdf() -> RhaiPdf {
RhaiPdf::new()
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn pdf_title(pdf: &mut RhaiPdf, title: String) -> Result<RhaiPdf, Box<EvalAltResult>> {
let owned = mem::take(pdf);
*pdf = owned.title(title);
Ok(pdf.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn pdf_description(pdf: &mut RhaiPdf, description: String) -> Result<RhaiPdf, Box<EvalAltResult>> {
let owned = mem::take(pdf);
*pdf = owned.description(description);
Ok(pdf.clone())
}
#[rhai_fn(name = "url", return_raw, global, pure)]
pub fn pdf_url(pdf: &mut RhaiPdf, url: String) -> Result<RhaiPdf, Box<EvalAltResult>> {
let owned = mem::take(pdf);
*pdf = owned.url(url);
Ok(pdf.clone())
}
#[rhai_fn(name = "page_count", return_raw, global, pure)]
pub fn pdf_page_count(pdf: &mut RhaiPdf, page_count: i64) -> Result<RhaiPdf, Box<EvalAltResult>> {
let owned = mem::take(pdf);
*pdf = owned.page_count(page_count as u32);
Ok(pdf.clone())
}
#[rhai_fn(get = "id", pure)]
pub fn get_pdf_id(pdf: &mut RhaiPdf) -> i64 { pdf.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_pdf_created_at(pdf: &mut RhaiPdf) -> i64 { pdf.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_pdf_modified_at(pdf: &mut RhaiPdf) -> i64 { pdf.base_data.modified_at }
#[rhai_fn(get = "title", pure)]
pub fn get_pdf_title(pdf: &mut RhaiPdf) -> String { pdf.title.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_pdf_description(pdf: &mut RhaiPdf) -> Option<String> { pdf.description.clone() }
#[rhai_fn(get = "url", pure)]
pub fn get_pdf_url(pdf: &mut RhaiPdf) -> String { pdf.url.clone() }
#[rhai_fn(get = "page_count", pure)]
pub fn get_pdf_page_count(pdf: &mut RhaiPdf) -> u32 { pdf.page_count }
// --- Markdown Functions ---
#[rhai_fn(name = "new_markdown")]
pub fn new_markdown() -> RhaiMarkdown {
RhaiMarkdown::new()
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn markdown_title(markdown: &mut RhaiMarkdown, title: String) -> Result<RhaiMarkdown, Box<EvalAltResult>> {
let owned = mem::take(markdown);
*markdown = owned.title(title);
Ok(markdown.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn markdown_description(markdown: &mut RhaiMarkdown, description: String) -> Result<RhaiMarkdown, Box<EvalAltResult>> {
let owned = mem::take(markdown);
*markdown = owned.description(description);
Ok(markdown.clone())
}
#[rhai_fn(name = "content", return_raw, global, pure)]
pub fn markdown_content(markdown: &mut RhaiMarkdown, content: String) -> Result<RhaiMarkdown, Box<EvalAltResult>> {
let owned = mem::take(markdown);
*markdown = owned.content(content);
Ok(markdown.clone())
}
#[rhai_fn(get = "id", pure)]
pub fn get_markdown_id(markdown: &mut RhaiMarkdown) -> i64 { markdown.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_markdown_created_at(markdown: &mut RhaiMarkdown) -> i64 { markdown.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_markdown_modified_at(markdown: &mut RhaiMarkdown) -> i64 { markdown.base_data.modified_at }
#[rhai_fn(get = "title", pure)]
pub fn get_markdown_title(markdown: &mut RhaiMarkdown) -> String { markdown.title.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_markdown_description(markdown: &mut RhaiMarkdown) -> Option<String> { markdown.description.clone() }
#[rhai_fn(get = "content", pure)]
pub fn get_markdown_content(markdown: &mut RhaiMarkdown) -> String { markdown.content.clone() }
// --- TocEntry Functions ---
#[rhai_fn(name = "new_toc_entry")]
pub fn new_toc_entry() -> RhaiTocEntry {
RhaiTocEntry::new()
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn toc_entry_title(entry: &mut RhaiTocEntry, title: String) -> Result<RhaiTocEntry, Box<EvalAltResult>> {
let owned = mem::take(entry);
*entry = owned.title(title);
Ok(entry.clone())
}
#[rhai_fn(name = "page", return_raw, global, pure)]
pub fn toc_entry_page(entry: &mut RhaiTocEntry, page: i64) -> Result<RhaiTocEntry, Box<EvalAltResult>> {
let owned = mem::take(entry);
*entry = owned.page(page as u32);
Ok(entry.clone())
}
#[rhai_fn(name = "add_subsection", return_raw, global, pure)]
pub fn toc_entry_add_subsection(entry: &mut RhaiTocEntry, subsection: RhaiTocEntry) -> Result<RhaiTocEntry, Box<EvalAltResult>> {
let owned = mem::take(entry);
*entry = owned.add_subsection(subsection);
Ok(entry.clone())
}
#[rhai_fn(get = "title", pure)]
pub fn get_toc_entry_title(entry: &mut RhaiTocEntry) -> String { entry.title.clone() }
#[rhai_fn(get = "page", pure)]
pub fn get_toc_entry_page(entry: &mut RhaiTocEntry) -> u32 { entry.page }
#[rhai_fn(get = "subsections", pure)]
pub fn get_toc_entry_subsections(entry: &mut RhaiTocEntry) -> Vec<RhaiTocEntry> { entry.subsections.clone() }
// --- Book Functions ---
#[rhai_fn(name = "new_book")]
pub fn new_book() -> RhaiBook {
RhaiBook::new()
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn book_title(book: &mut RhaiBook, title: String) -> Result<RhaiBook, Box<EvalAltResult>> {
let owned = mem::take(book);
*book = owned.title(title);
Ok(book.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn book_description(book: &mut RhaiBook, description: String) -> Result<RhaiBook, Box<EvalAltResult>> {
let owned = mem::take(book);
*book = owned.description(description);
Ok(book.clone())
}
#[rhai_fn(name = "add_page", return_raw, global, pure)]
pub fn book_add_page(book: &mut RhaiBook, content: String) -> Result<RhaiBook, Box<EvalAltResult>> {
let owned = mem::take(book);
*book = owned.add_page(content);
Ok(book.clone())
}
#[rhai_fn(name = "add_toc_entry", return_raw, global, pure)]
pub fn book_add_toc_entry(book: &mut RhaiBook, entry: RhaiTocEntry) -> Result<RhaiBook, Box<EvalAltResult>> {
let owned = mem::take(book);
*book = owned.add_toc_entry(entry);
Ok(book.clone())
}
#[rhai_fn(get = "id", pure)]
pub fn get_book_id(book: &mut RhaiBook) -> i64 { book.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_book_created_at(book: &mut RhaiBook) -> i64 { book.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_book_modified_at(book: &mut RhaiBook) -> i64 { book.base_data.modified_at }
#[rhai_fn(get = "title", pure)]
pub fn get_book_title(book: &mut RhaiBook) -> String { book.title.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_book_description(book: &mut RhaiBook) -> Option<String> { book.description.clone() }
#[rhai_fn(get = "table_of_contents", pure)]
pub fn get_book_table_of_contents(book: &mut RhaiBook) -> Vec<RhaiTocEntry> { book.table_of_contents.clone() }
#[rhai_fn(get = "pages", pure)]
pub fn get_book_pages(book: &mut RhaiBook) -> Vec<String> { book.pages.clone() }
// --- Slides Functions ---
#[rhai_fn(name = "new_slides")]
pub fn new_slides() -> RhaiSlides {
RhaiSlides::new()
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn slides_title(slides: &mut RhaiSlides, title: String) -> Result<RhaiSlides, Box<EvalAltResult>> {
let owned = mem::take(slides);
*slides = owned.title(title);
Ok(slides.clone())
}
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn slides_description(slides: &mut RhaiSlides, description: String) -> Result<RhaiSlides, Box<EvalAltResult>> {
let owned = mem::take(slides);
*slides = owned.description(description);
Ok(slides.clone())
}
#[rhai_fn(name = "add_slide", return_raw, global, pure)]
pub fn slides_add_slide(slides: &mut RhaiSlides, url: String, title: String) -> Result<RhaiSlides, Box<EvalAltResult>> {
let owned = mem::take(slides);
let title_opt = if title.is_empty() { None } else { Some(title) };
*slides = owned.add_slide(url, title_opt);
Ok(slides.clone())
}
#[rhai_fn(name = "add_slide", return_raw, global, pure)]
pub fn slides_add_slide_no_title(slides: &mut RhaiSlides, url: String) -> Result<RhaiSlides, Box<EvalAltResult>> {
let owned = mem::take(slides);
*slides = owned.add_slide(url, None);
Ok(slides.clone())
}
#[rhai_fn(get = "id", pure)]
pub fn get_slides_id(slides: &mut RhaiSlides) -> i64 { slides.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_slides_created_at(slides: &mut RhaiSlides) -> i64 { slides.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_slides_modified_at(slides: &mut RhaiSlides) -> i64 { slides.base_data.modified_at }
#[rhai_fn(get = "title", pure)]
pub fn get_slides_title(slides: &mut RhaiSlides) -> String { slides.title.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_slides_description(slides: &mut RhaiSlides) -> Option<String> { slides.description.clone() }
#[rhai_fn(get = "slide_urls", pure)]
pub fn get_slides_slide_urls(slides: &mut RhaiSlides) -> Vec<String> { slides.slide_urls.clone() }
#[rhai_fn(get = "slide_titles", pure)]
pub fn get_slides_slide_titles(slides: &mut RhaiSlides) -> Vec<Option<String>> { slides.slide_titles.clone() }
}
pub fn register_library_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
let module = exported_module!(rhai_library_module);
engine.register_global_module(module.into());
let mut db_module = Module::new();
register_json_method::<RhaiCollection>(engine);
register_json_method::<RhaiImage>(engine);
register_json_method::<RhaiPdf>(engine);
register_json_method::<RhaiMarkdown>(engine);
register_json_method::<RhaiBook>(engine);
register_json_method::<RhaiSlides>(engine);
register_json_method::<RhaiTocEntry>(engine);
// Register .json() method for our custom CollectionArray type
register_json_method::<RhaiCollectionArray>(engine);
// --- Collection DB Functions ---
let db_clone = db.clone();
db_module.set_native_fn("save_collection", move |collection: RhaiCollection| -> Result<RhaiCollection, Box<EvalAltResult>> {
let result = db_clone.set(&collection)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(result.1)
});
let db_clone = db.clone();
db_module.set_native_fn("get_collection", move |id: i64| -> Result<RhaiCollection, Box<EvalAltResult>> {
let collection_id = id_from_i64_to_u32(id)?;
db_clone.get_by_id(collection_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Collection with ID {} not found", collection_id).into(), Position::NONE)))
});
let db_clone_list_collections = db.clone();
db_module.set_native_fn("list_collections", move || -> Result<RhaiCollectionArray, Box<EvalAltResult>> {
let collections_vec: Vec<RhaiCollection> = db_clone_list_collections
.collection::<RhaiCollection>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error (list_collections - access): {:?}", e).into(), Position::NONE)))?
.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error (list_collections - get_all): {:?}", e).into(), Position::NONE)))?;
Ok(RhaiCollectionArray(collections_vec)) // Wrap in RhaiCollectionArray
});
let db_clone = db.clone();
db_module.set_native_fn("delete_collection", move |id: i64| -> Result<(), Box<EvalAltResult>> {
let collection_id = id_from_i64_to_u32(id)?;
db_clone.collection::<RhaiCollection>().unwrap().delete_by_id(collection_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(())
});
// --- Image DB Functions ---
let db_clone = db.clone();
db_module.set_native_fn("save_image", move |image: RhaiImage| -> Result<RhaiImage, Box<EvalAltResult>> {
let result = db_clone.set(&image)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(result.1)
});
let db_clone = db.clone();
db_module.set_native_fn("get_image", move |id: i64| -> Result<RhaiImage, Box<EvalAltResult>> {
let image_id = id_from_i64_to_u32(id)?;
db_clone.get_by_id(image_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Image with ID {} not found", image_id).into(), Position::NONE)))
});
let db_clone = db.clone();
db_module.set_native_fn("delete_image", move |id: i64| -> Result<(), Box<EvalAltResult>> {
let image_id = id_from_i64_to_u32(id)?;
db_clone.collection::<RhaiImage>().unwrap().delete_by_id(image_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(())
});
// --- Pdf DB Functions ---
let db_clone = db.clone();
db_module.set_native_fn("save_pdf", move |pdf: RhaiPdf| -> Result<RhaiPdf, Box<EvalAltResult>> {
let result = db_clone.set(&pdf)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(result.1)
});
let db_clone = db.clone();
db_module.set_native_fn("get_pdf", move |id: i64| -> Result<RhaiPdf, Box<EvalAltResult>> {
let pdf_id = id_from_i64_to_u32(id)?;
db_clone.get_by_id(pdf_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Pdf with ID {} not found", pdf_id).into(), Position::NONE)))
});
let db_clone = db.clone();
db_module.set_native_fn("delete_pdf", move |id: i64| -> Result<(), Box<EvalAltResult>> {
let pdf_id = id_from_i64_to_u32(id)?;
db_clone.collection::<RhaiPdf>().unwrap().delete_by_id(pdf_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(())
});
// --- Markdown DB Functions ---
let db_clone = db.clone();
db_module.set_native_fn("save_markdown", move |markdown: RhaiMarkdown| -> Result<RhaiMarkdown, Box<EvalAltResult>> {
let result = db_clone.set(&markdown)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(result.1)
});
let db_clone = db.clone();
db_module.set_native_fn("get_markdown", move |id: i64| -> Result<RhaiMarkdown, Box<EvalAltResult>> {
let markdown_id = id_from_i64_to_u32(id)?;
db_clone.get_by_id(markdown_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Markdown with ID {} not found", markdown_id).into(), Position::NONE)))
});
let db_clone = db.clone();
db_module.set_native_fn("delete_markdown", move |id: i64| -> Result<(), Box<EvalAltResult>> {
let markdown_id = id_from_i64_to_u32(id)?;
db_clone.collection::<RhaiMarkdown>().unwrap().delete_by_id(markdown_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(())
});
// --- Book DB Functions ---
let db_clone = db.clone();
db_module.set_native_fn("save_book", move |book: RhaiBook| -> Result<RhaiBook, Box<EvalAltResult>> {
let result = db_clone.set(&book)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(result.1)
});
let db_clone = db.clone();
db_module.set_native_fn("get_book", move |id: i64| -> Result<RhaiBook, Box<EvalAltResult>> {
let book_id = id_from_i64_to_u32(id)?;
db_clone.get_by_id(book_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Book with ID {} not found", book_id).into(), Position::NONE)))
});
let db_clone = db.clone();
db_module.set_native_fn("delete_book", move |id: i64| -> Result<(), Box<EvalAltResult>> {
let book_id = id_from_i64_to_u32(id)?;
db_clone.collection::<RhaiBook>().unwrap().delete_by_id(book_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(())
});
// --- Slides DB Functions ---
let db_clone = db.clone();
db_module.set_native_fn("save_slides", move |slides: RhaiSlides| -> Result<RhaiSlides, Box<EvalAltResult>> {
let result = db_clone.set(&slides)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(result.1)
});
let db_clone = db.clone();
db_module.set_native_fn("get_slides", move |id: i64| -> Result<RhaiSlides, Box<EvalAltResult>> {
let slides_id = id_from_i64_to_u32(id)?;
db_clone.get_by_id(slides_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Slides with ID {} not found", slides_id).into(), Position::NONE)))
});
let db_clone = db.clone();
db_module.set_native_fn("delete_slides", move |id: i64| -> Result<(), Box<EvalAltResult>> {
let slides_id = id_from_i64_to_u32(id)?;
db_clone.collection::<RhaiSlides>().unwrap().delete_by_id(slides_id)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error: {:?}", e).into(), Position::NONE)))?;
Ok(())
});
engine.register_global_module(db_module.into());
}

View File

@ -3,8 +3,10 @@ pub mod core;
pub mod userexample;
// pub mod productexample; // Temporarily remove as files are missing
pub mod calendar;
pub mod circle;
pub mod governance;
pub mod finance;
pub mod library;
pub mod legal;
pub mod flow;
pub mod biz;
@ -15,18 +17,25 @@ pub use core::Comment;
pub use userexample::User;
// pub use productexample::Product; // Temporarily remove
pub use calendar::{Calendar, Event, Attendee, AttendanceStatus};
pub use circle::{Circle};
pub use governance::{Proposal, ProposalStatus, VoteEventStatus, Ballot, VoteOption, AttachedFile};
pub use finance::{Account, Asset, AssetType};
pub use finance::marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus};
pub use legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
pub use flow::{Flow, FlowStep, SignatureRequirement};
pub use biz::{Sale, SaleItem, SaleStatus};
pub use library::items::{Image, Pdf, Markdown};
pub use library::collection::Collection;
pub use flow::register_flow_rhai_module;
#[cfg(feature = "rhai")]
pub use calendar::register_calendar_rhai_module;
#[cfg(feature = "rhai")]
pub use circle::register_circle_rhai_module;
pub use legal::register_legal_rhai_module;
#[cfg(feature = "rhai")]
pub use biz::register_biz_rhai_module;
#[cfg(feature = "rhai")]
pub use projects::register_projects_rhai_module;
#[cfg(feature = "rhai")]
pub use library::register_library_rhai_module;