move rhai wrappers of models from rhailib

This commit is contained in:
Timur Gordon
2025-08-21 14:05:01 +02:00
parent 58ed59cd12
commit cedea2f305
19 changed files with 2110 additions and 393 deletions

View File

@@ -1,412 +1,155 @@
use crate::db::Db;
use rhailib_macros::{
register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn,
};
use rhai::plugin::*;
use rhai::{Array, CustomType, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map, Module};
use std::collections::HashMap;
use std::sync::Arc;
use super::circle::{Circle, ThemeData};
use crate::models::circle::Circle;
type RhaiCircle = Circle;
type RhaiThemeData = ThemeData;
use crate::db::Collection;
use crate::db::hero::OurDB;
use serde::Serialize;
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
T: CustomType + Clone + Serialize,
{
let to_json_fn = |obj: &mut T| -> Result<String, Box<EvalAltResult>> {
serde_json::to_string(obj).map_err(|e| e.to_string().into())
};
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_theme_data_module {
#[rhai_fn(name = "new_theme_data")]
pub fn new_theme_data() -> RhaiThemeData {
ThemeData::default()
}
// --- Setters for ThemeData ---
#[rhai_fn(name = "primary_color", return_raw, global, pure)]
pub fn set_primary_color(
theme: &mut RhaiThemeData,
color: String,
) -> Result<RhaiThemeData, Box<EvalAltResult>> {
let mut owned_theme = mem::take(theme);
owned_theme.primary_color = color;
*theme = owned_theme;
Ok(theme.clone())
}
#[rhai_fn(name = "background_color", return_raw, global, pure)]
pub fn set_background_color(
theme: &mut RhaiThemeData,
color: String,
) -> Result<RhaiThemeData, Box<EvalAltResult>> {
let mut owned_theme = mem::take(theme);
owned_theme.background_color = color;
*theme = owned_theme;
Ok(theme.clone())
}
#[rhai_fn(name = "background_pattern", return_raw, global, pure)]
pub fn set_background_pattern(
theme: &mut RhaiThemeData,
pattern: String,
) -> Result<RhaiThemeData, Box<EvalAltResult>> {
let mut owned_theme = mem::take(theme);
owned_theme.background_pattern = pattern;
*theme = owned_theme;
Ok(theme.clone())
}
#[rhai_fn(name = "logo_symbol", return_raw, global, pure)]
pub fn set_logo_symbol(
theme: &mut RhaiThemeData,
symbol: String,
) -> Result<RhaiThemeData, Box<EvalAltResult>> {
let mut owned_theme = mem::take(theme);
owned_theme.logo_symbol = symbol;
*theme = owned_theme;
Ok(theme.clone())
}
#[rhai_fn(name = "logo_url", return_raw, global, pure)]
pub fn set_logo_url(
theme: &mut RhaiThemeData,
url: String,
) -> Result<RhaiThemeData, Box<EvalAltResult>> {
let mut owned_theme = mem::take(theme);
owned_theme.logo_url = url;
*theme = owned_theme;
Ok(theme.clone())
}
#[rhai_fn(name = "nav_dashboard_visible", return_raw, global, pure)]
pub fn set_nav_dashboard_visible(
theme: &mut RhaiThemeData,
visible: bool,
) -> Result<RhaiThemeData, Box<EvalAltResult>> {
let mut owned_theme = mem::take(theme);
owned_theme.nav_dashboard_visible = visible;
*theme = owned_theme;
Ok(theme.clone())
}
#[rhai_fn(name = "nav_timeline_visible", return_raw, global, pure)]
pub fn set_nav_timeline_visible(
theme: &mut RhaiThemeData,
visible: bool,
) -> Result<RhaiThemeData, Box<EvalAltResult>> {
let mut owned_theme = mem::take(theme);
owned_theme.nav_timeline_visible = visible;
*theme = owned_theme;
Ok(theme.clone())
}
// --- Getters for ThemeData ---
#[rhai_fn(name = "get_primary_color", pure)]
pub fn get_primary_color(theme: &mut RhaiThemeData) -> String {
theme.primary_color.clone()
}
#[rhai_fn(name = "get_background_color", pure)]
pub fn get_background_color(theme: &mut RhaiThemeData) -> String {
theme.background_color.clone()
}
#[rhai_fn(name = "get_background_pattern", pure)]
pub fn get_background_pattern(theme: &mut RhaiThemeData) -> String {
theme.background_pattern.clone()
}
#[rhai_fn(name = "get_logo_symbol", pure)]
pub fn get_logo_symbol(theme: &mut RhaiThemeData) -> String {
theme.logo_symbol.clone()
}
#[rhai_fn(name = "get_logo_url", pure)]
pub fn get_logo_url(theme: &mut RhaiThemeData) -> String {
theme.logo_url.clone()
}
#[rhai_fn(name = "get_nav_dashboard_visible", pure)]
pub fn get_nav_dashboard_visible(theme: &mut RhaiThemeData) -> bool {
theme.nav_dashboard_visible
}
#[rhai_fn(name = "get_nav_timeline_visible", pure)]
pub fn get_nav_timeline_visible(theme: &mut RhaiThemeData) -> bool {
theme.nav_timeline_visible
}
}
use crate::db::Collection;
use crate::models::circle::ThemeData;
#[export_module]
mod rhai_circle_module {
// --- Circle Functions ---
#[rhai_fn(name = "new_circle")]
pub fn new_circle() -> RhaiCircle {
Circle::new()
use super::RhaiCircle;
// this one configures the users own circle
#[rhai_fn(name = "configure", return_raw)]
pub fn configure() -> Result<RhaiCircle, Box<EvalAltResult>> {
Ok(Circle::new())
}
/// Sets the circle title
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn circle_title(
#[rhai_fn(name = "new_circle", return_raw)]
pub fn new_circle() -> Result<RhaiCircle, Box<EvalAltResult>> {
Ok(Circle::new())
}
#[rhai_fn(name = "set_title", return_raw)]
pub fn set_title(
circle: &mut RhaiCircle,
title: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.title(title);
let owned = std::mem::take(circle);
*circle = owned.title(title);
Ok(circle.clone())
}
/// Sets the circle ws_url
#[rhai_fn(name = "ws_url", return_raw, global, pure)]
pub fn circle_ws_url(
#[rhai_fn(name = "set_ws_url", return_raw)]
pub fn set_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);
let owned = std::mem::take(circle);
*circle = owned.ws_url(ws_url);
Ok(circle.clone())
}
/// Sets the circle description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn circle_description(
#[rhai_fn(name = "set_description", return_raw)]
pub fn set_description(
circle: &mut RhaiCircle,
description: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.description(description);
let owned = std::mem::take(circle);
*circle = owned.description(description);
Ok(circle.clone())
}
/// Sets the circle logo
#[rhai_fn(name = "logo", return_raw, global, pure)]
pub fn circle_logo(
#[rhai_fn(name = "set_logo", return_raw)]
pub fn set_logo(
circle: &mut RhaiCircle,
logo: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.logo(logo);
let owned = std::mem::take(circle);
*circle = owned.logo(logo);
Ok(circle.clone())
}
/// Sets the circle theme
#[rhai_fn(name = "theme", return_raw, global, pure)]
pub fn circle_theme(
#[rhai_fn(name = "set_theme", return_raw)]
pub fn set_theme(
circle: &mut RhaiCircle,
theme: RhaiThemeData,
theme: ThemeData,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.theme(theme);
let owned = std::mem::take(circle);
*circle = owned.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(
#[rhai_fn(name = "add_circle", return_raw)]
pub fn add_circle(
circle: &mut RhaiCircle,
added_circle: String,
new_circle: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.add_circle(added_circle);
let owned = std::mem::take(circle);
*circle = owned.add_circle(new_circle);
Ok(circle.clone())
}
/// Adds an attendee to the circle
#[rhai_fn(name = "add_member", return_raw, global, pure)]
pub fn circle_add_member(
#[rhai_fn(name = "add_member", return_raw)]
pub fn add_member(
circle: &mut RhaiCircle,
added_member: String,
member: String,
) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned_circle = mem::take(circle);
*circle = owned_circle.add_member(added_member);
let owned = std::mem::take(circle);
*circle = owned.add_member(member);
Ok(circle.clone())
}
// Circle Getters
#[rhai_fn(name = "get_id", pure)]
pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 {
circle.base_data.id as i64
// --- Getters ---
#[rhai_fn(name = "get_id")]
pub fn get_id(c: &mut RhaiCircle) -> i64 {
c.base_data.id as i64
}
#[rhai_fn(name = "get_created_at", pure)]
pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 {
circle.base_data.created_at
#[rhai_fn(name = "get_title")]
pub fn get_title(c: &mut RhaiCircle) -> String {
c.title.clone()
}
#[rhai_fn(name = "get_modified_at", pure)]
pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 {
circle.base_data.modified_at
#[rhai_fn(name = "get_ws_url")]
pub fn get_ws_url(c: &mut RhaiCircle) -> String {
c.ws_url.clone()
}
#[rhai_fn(name = "get_title", pure)]
pub fn get_circle_title(circle: &mut RhaiCircle) -> String {
circle.title.clone()
#[rhai_fn(name = "get_description")]
pub fn get_description(c: &mut RhaiCircle) -> Option<String> {
c.description.clone()
}
#[rhai_fn(name = "get_description", pure)]
pub fn get_circle_description(circle: &mut RhaiCircle) -> Option<String> {
circle.description.clone()
#[rhai_fn(name = "get_logo")]
pub fn get_logo(c: &mut RhaiCircle) -> Option<String> {
c.logo.clone()
}
#[rhai_fn(name = "get_circles", pure)]
pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec<String> {
circle.circles.clone()
#[rhai_fn(name = "get_circles")]
pub fn get_circles(c: &mut RhaiCircle) -> Array {
c.circles.iter().map(|s| Dynamic::from(s.clone())).collect()
}
#[rhai_fn(name = "get_ws_url", pure)]
pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String {
circle.ws_url.clone()
}
#[rhai_fn(name = "get_logo", pure)]
pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option<String> {
circle.logo.clone()
}
#[rhai_fn(name = "get_theme", pure)]
pub fn get_circle_theme(circle: &mut RhaiCircle) -> RhaiThemeData {
circle.theme.clone()
#[rhai_fn(name = "get_members")]
pub fn get_members(c: &mut RhaiCircle) -> Array {
c.members.iter().map(|s| Dynamic::from(s.clone())).collect()
}
}
pub fn register_circle_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
engine.build_type::<RhaiCircle>();
engine.build_type::<RhaiThemeData>();
pub fn register_circle_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_circle_module);
let mut db_module = Module::new();
let circle_module = exported_module!(rhai_circle_module);
let theme_data_module = exported_module!(rhai_theme_data_module);
engine.register_global_module(circle_module.into());
engine.register_global_module(theme_data_module.into());
register_json_method::<Circle>(engine);
register_json_method::<ThemeData>(engine);
// 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>> {
let result = db_clone_set_circle.set(&circle).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error set_circle: {}", e).into(),
Position::NONE,
))
})?;
Ok(result.1)
},
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_circle",
resource_type_str: "Circle",
rhai_return_rust_type: crate::models::circle::Circle
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_circle",
resource_type_str: "Circle",
rhai_return_rust_type: crate::models::circle::Circle
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_circle",
resource_type_str: "Circle",
rhai_return_rust_type: crate::models::circle::Circle
);
let db_clone_delete_circle = db.clone();
db_module.set_native_fn(
"delete_circle",
move |circle: Circle| -> Result<(), Box<EvalAltResult>> {
let result = db_clone_delete_circle
.collection::<Circle>()
.expect("can open circle collection")
.delete_by_id(circle.base_data.id)
.expect("can delete circle");
Ok(result)
},
);
let db_clone_get_circle = db.clone();
db_module.set_native_fn(
"get_circle",
move || -> Result<Circle, Box<EvalAltResult>> {
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,
)))
}
},
);
// --- Collection DB Functions ---
let db_clone = db.clone();
db_module.set_native_fn(
"save_circle",
move |circle: RhaiCircle| -> Result<RhaiCircle, Box<EvalAltResult>> {
let result = db_clone.set(&circle).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("DB Error: {:?}", e).into(),
Position::NONE,
))
})?;
Ok(result.1)
},
);
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)?;
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,
))
})
},
);
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))
},
);
engine.register_global_module(db_module.into());
println!("Successfully registered circle Rhai module using export_module approach.");
engine.register_global_module(module.into());
}