use crate::db::Db; use rhai::plugin::*; use rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, Position, TypeBuilder}; use serde::Serialize; use serde_json; use std::mem; use std::sync::Arc; use super::collection::Collection as RhaiCollection; use super::items::{ Book as RhaiBook, Image as RhaiImage, Markdown as RhaiMarkdown, Pdf as RhaiPdf, Slide as RhaiSlide, Slideshow as RhaiSlides, TocEntry as RhaiTocEntry, }; use crate::db::Collection as DbCollectionTrait; use crate::db::hero::OurDB; // Helper to convert i64 from Rhai to u32 for IDs fn id_from_i64_to_u32(id_i64: i64) -> Result> { u32::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(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> { // 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_pretty(obj).map_err(|e| e.to_string().into()) }; // Register the function as a method named "json" for the type 'T'. engine.build_type::().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); impl From> for RhaiCollectionArray { fn from(collections: Vec) -> Self { RhaiCollectionArray(collections) } } #[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> { 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> { 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> { 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> { 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> { 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> { 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> { 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 { collection.description.clone() } #[rhai_fn(get = "images", pure)] pub fn get_collection_images(collection: &mut RhaiCollection) -> Vec { 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 { 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 { 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 { 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 { 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> { 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> { 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> { 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> { 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> { 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 { 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> { 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> { 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> { 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> { 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 { 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> { 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> { 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> { 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 { 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> { 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> { 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> { 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 { 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> { 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> { 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> { 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> { 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 { book.description.clone() } #[rhai_fn(get = "table_of_contents", pure)] pub fn get_book_table_of_contents(book: &mut RhaiBook) -> Vec { book.table_of_contents.clone() } #[rhai_fn(get = "pages", pure)] pub fn get_book_pages(book: &mut RhaiBook) -> Vec { book.pages.clone() } // --- Slideshow Functions --- #[rhai_fn(name = "new_slide")] pub fn new_slide() -> RhaiSlide { RhaiSlide::new() } #[rhai_fn(name = "title", return_raw, global, pure)] pub fn slide_title( slide: &mut RhaiSlide, title: String, ) -> Result> { let owned = mem::take(slide); *slide = owned.title(title); Ok(slide.clone()) } #[rhai_fn(name = "description", return_raw, global, pure)] pub fn slide_description( slide: &mut RhaiSlide, description: String, ) -> Result> { let owned = mem::take(slide); *slide = owned.description(description); Ok(slide.clone()) } #[rhai_fn(name = "url", return_raw, global, pure)] pub fn slide_url(slide: &mut RhaiSlide, url: String) -> Result> { let owned = mem::take(slide); *slide = owned.url(url); Ok(slide.clone()) } // --- Slideshow 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> { 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> { 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, slide: RhaiSlide, ) -> Result> { let owned = mem::take(slides); *slides = owned.add_slide(slide); 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 { slides.description.clone() } #[rhai_fn(get = "slides", pure)] pub fn get_slides_slides(slides: &mut RhaiSlides) -> Vec { slides.slides.clone() } } pub fn register_library_rhai_module(engine: &mut Engine) { let module = exported_module!(rhai_library_module); engine.register_global_module(module.into()); let mut db_module = Module::new(); register_json_method::(engine); register_json_method::(engine); register_json_method::(engine); register_json_method::(engine); register_json_method::(engine); register_json_method::(engine); register_json_method::(engine); // Register .json() method for our custom CollectionArray type register_json_method::(engine); // --- Collection DB Functions --- let db_clone = db.clone(); db_module.set_native_fn( "save_collection", move |collection: RhaiCollection| -> Result> { 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> { 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> { let collections_vec: Vec = db_clone_list_collections .collection::() .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> { let collection_id = id_from_i64_to_u32(id)?; db_clone .collection::() .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> { 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> { 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> { let image_id = id_from_i64_to_u32(id)?; db_clone .collection::() .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> { 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> { 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> { let pdf_id = id_from_i64_to_u32(id)?; db_clone .collection::() .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> { 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> { 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> { let markdown_id = id_from_i64_to_u32(id)?; db_clone .collection::() .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> { 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> { 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> { let book_id = id_from_i64_to_u32(id)?; db_clone .collection::() .unwrap() .delete_by_id(book_id) .map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("DB Error: {:?}", e).into(), Position::NONE, )) })?; Ok(()) }, ); // --- Slideshow DB Functions --- let db_clone = db.clone(); db_module.set_native_fn( "save_slides", move |slides: RhaiSlides| -> Result> { 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> { 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!("Slideshow 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> { let slides_id = id_from_i64_to_u32(id)?; db_clone .collection::() .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()); }