db new models

This commit is contained in:
timurgordon 2025-06-18 01:56:24 +03:00
parent 00c4e6a1eb
commit 6b3cbfc4b2
9 changed files with 602 additions and 0 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
target/
*.DS_Store
*.wasm
herovm_build/

BIN
heromodels/.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,58 @@
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};
/// Represents an event in a contact
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Access {
/// Base model data
pub base_data: BaseModelData,
#[index]
pub object_id: u32,
pub circle_id: u32,
pub contact_id: u32,
pub group_id: u32,
pub expires_at: Option<u64>,
}
impl Access {
pub fn new() -> Self {
Access {
base_data: BaseModelData::new(),
object_id: 0,
circle_id: 0,
contact_id: 0,
group_id: 0,
expires_at: None,
}
}
pub fn object_id(mut self, object_id: u32) -> Self {
self.object_id = object_id;
self
}
pub fn contact_id(mut self, contact_id: u32) -> Self {
self.contact_id = contact_id;
self
}
pub fn group_id(mut self, group_id: u32) -> Self {
self.group_id = group_id;
self
}
pub fn circle_id(mut self, circle_id: u32) -> Self {
self.circle_id = circle_id;
self
}
pub fn expires_at(mut self, expires_at: Option<u64>) -> Self {
self.expires_at = expires_at;
self
}
}

View File

@ -0,0 +1,7 @@
// Export contact module
pub mod access;
pub mod rhai;
// Re-export contact, Group from the inner contact module (contact.rs) within src/models/contact/mod.rs
pub use self::access::{Access};
pub use rhai::register_access_rhai_module;

View File

@ -0,0 +1,177 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use super::access::{Access};
type RhaiAccess = Access;
use crate::db::hero::OurDB;
use crate::db::Collection;
// 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_access_module {
// --- Access Functions ---
#[rhai_fn(name = "new_access", return_raw)]
pub fn new_access() -> Result<RhaiAccess, Box<EvalAltResult>> {
let access = Access::new();
Ok(access)
}
/// Sets the access name
#[rhai_fn(name = "object_id", return_raw, global, pure)]
pub fn access_object_id(access: &mut RhaiAccess, object_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.object_id(object_id);
Ok(access.clone())
}
#[rhai_fn(name = "circle_id", return_raw, global, pure)]
pub fn access_circle_id(access: &mut RhaiAccess, circle_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.circle_id(circle_id);
Ok(access.clone())
}
#[rhai_fn(name = "group_id", return_raw, global, pure)]
pub fn access_group_id(access: &mut RhaiAccess, group_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.group_id(group_id);
Ok(access.clone())
}
#[rhai_fn(name = "contact_id", return_raw, global, pure)]
pub fn access_contact_id(access: &mut RhaiAccess, contact_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.contact_id(contact_id);
Ok(access.clone())
}
#[rhai_fn(name = "expires_at", return_raw, global, pure)]
pub fn access_expires_at(access: &mut RhaiAccess, expires_at: Option<u64>) -> Result<RhaiAccess, Box<EvalAltResult>> {
// Create a default Access to replace the taken one
let default_access = Access::new();
// Take ownership of the access, apply the builder method, then put it back
let owned_access = std::mem::replace(access, default_access);
*access = owned_access.expires_at(expires_at);
Ok(access.clone())
}
// Access Getters
#[rhai_fn(get = "id", pure)]
pub fn get_access_id(access: &mut RhaiAccess) -> i64 { access.base_data.id as i64 }
#[rhai_fn(get = "object_id", pure)]
pub fn get_access_object_id(access: &mut RhaiAccess) -> i64 { access.object_id as i64 }
#[rhai_fn(get = "circle_id", pure)]
pub fn get_access_circle_id(access: &mut RhaiAccess) -> i64 { access.circle_id as i64 }
#[rhai_fn(get = "group_id", pure)]
pub fn get_access_group_id(access: &mut RhaiAccess) -> i64 { access.group_id as i64 }
#[rhai_fn(get = "contact_id", pure)]
pub fn get_access_contact_id(access: &mut RhaiAccess) -> i64 { access.contact_id as i64 }
#[rhai_fn(get = "expires_at", pure)]
pub fn get_access_expires_at(access: &mut RhaiAccess) -> i64 { access.expires_at.unwrap_or(0) as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_access_created_at(access: &mut RhaiAccess) -> i64 { access.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_access_modified_at(access: &mut RhaiAccess) -> i64 { access.base_data.modified_at }
}
pub fn register_access_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_access_module);
engine.register_global_module(module.into());
// Create a module for database functions
let mut db_module = Module::new();
let db_clone_set_access = db.clone();
db_module.set_native_fn("save_access", move |access: Access| -> Result<Access, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_access.set(&access)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_access: {}", e).into(), Position::NONE)))?;
// Return the updated access with the correct ID
Ok(result.1)
});
// Manually register database functions as they need to capture 'db'
let db_clone_delete_access = db.clone();
db_module.set_native_fn("delete_access", move |access: Access| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_access.collection::<Access>()
.expect("can open access collection")
.delete_by_id(access.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
});
let db_clone_get_access = db.clone();
db_module.set_native_fn("get_access_by_id", move |id_i64: INT| -> Result<Access, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_access.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_access_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Access with ID {} not found", id_u32).into(), Position::NONE)))
});
// Add list_accesss function to get all accesss
let db_clone_list_accesss = db.clone();
db_module.set_native_fn("list_accesss", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_accesss.collection::<Access>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get access collection: {:?}", e).into(),
Position::NONE
)))?;
let accesss = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all accesss: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for access in accesss {
array.push(Dynamic::from(access));
}
Ok(Dynamic::from(array))
});
// Register the database module globally
engine.register_global_module(db_module.into());
println!("Successfully registered access Rhai module using export_module approach.");
}

View File

@ -0,0 +1,115 @@
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};
/// Represents an event in a contact
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
pub struct Contact {
/// Base model data
pub base_data: BaseModelData,
#[index]
pub name: String,
pub description: Option<String>,
pub address: String,
pub phone: String,
pub email: String,
pub notes: Option<String>,
pub circle: String,
}
impl Contact {
pub fn new() -> Self {
Contact {
base_data: BaseModelData::new(),
name: String::new(),
description: None,
address: String::new(),
phone: String::new(),
email: String::new(),
notes: None,
circle: String::new(),
}
}
pub fn name(mut self, name: impl ToString) -> Self {
self.name = name.to_string();
self
}
pub fn description(mut self, description: impl ToString) -> Self {
self.description = Some(description.to_string());
self
}
pub fn address(mut self, address: impl ToString) -> Self {
self.address = address.to_string();
self
}
pub fn phone(mut self, phone: impl ToString) -> Self {
self.phone = phone.to_string();
self
}
pub fn email(mut self, email: impl ToString) -> Self {
self.email = email.to_string();
self
}
pub fn notes(mut self, notes: impl ToString) -> Self {
self.notes = Some(notes.to_string());
self
}
pub fn circle(mut self, circle: impl ToString) -> Self {
self.circle = circle.to_string();
self
}
}
/// Represents an event in a contact
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)]
pub struct Group {
/// Base model data
pub base_data: BaseModelData,
#[index]
pub name: String,
pub description: Option<String>,
pub contacts: Vec<u32>,
}
impl Group {
pub fn new() -> Self {
Group {
base_data: BaseModelData::new(),
name: String::new(),
description: None,
contacts: Vec::new(),
}
}
pub fn name(mut self, name: impl ToString) -> Self {
self.name = name.to_string();
self
}
pub fn description(mut self, description: impl ToString) -> Self {
self.description = Some(description.to_string());
self
}
pub fn contacts(mut self, contacts: Vec<u32>) -> Self {
self.contacts = contacts;
self
}
pub fn add_contact(mut self, contact: u32) -> Self {
self.contacts.push(contact);
self
}
}

View File

@ -0,0 +1,7 @@
// Export contact module
pub mod contact;
pub mod rhai;
// Re-export contact, Group from the inner contact module (contact.rs) within src/models/contact/mod.rs
pub use self::contact::{Contact, Group};
pub use rhai::register_contact_rhai_module;

View File

@ -0,0 +1,235 @@
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
use std::sync::Arc;
use std::mem;
use crate::db::Db;
use super::contact::{Group, Contact};
type RhaiGroup = Group;
type RhaiContact = Contact;
use crate::db::hero::OurDB;
use crate::db::Collection;
// 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_contact_module {
// --- Event Functions ---
#[rhai_fn(name = "new_group")]
pub fn new_group() -> RhaiGroup {
Group::new()
}
/// Sets the event title
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn group_name(group: &mut RhaiGroup, name: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
let owned_group = mem::take(group);
*group = owned_group.name(name);
Ok(group.clone())
}
/// Sets the event description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn group_description(group: &mut RhaiGroup, description: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
let owned_group = mem::take(group);
*group = owned_group.description(description);
Ok(group.clone())
}
/// Adds an attendee to the event
#[rhai_fn(name = "add_contact", return_raw, global, pure)]
pub fn group_add_contact(group: &mut RhaiGroup, contact_id: i64) -> Result<RhaiGroup, Box<EvalAltResult>> {
// Use take to get ownership of the event
let owned_group = mem::take(group);
*group = owned_group.add_contact(contact_id as u32);
Ok(group.clone())
}
#[rhai_fn(get = "contacts", pure)]
pub fn get_group_contacts(group: &mut RhaiGroup) -> Vec<i64> { group.contacts.clone().into_iter().map(|id| id as i64).collect() }
// Group Getters
#[rhai_fn(get = "id", pure)]
pub fn get_group_id(group: &mut RhaiGroup) -> i64 { group.base_data.id as i64 }
#[rhai_fn(get = "created_at", pure)]
pub fn get_group_created_at(group: &mut RhaiGroup) -> i64 { group.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_group_modified_at(group: &mut RhaiGroup) -> i64 { group.base_data.modified_at }
#[rhai_fn(get = "name", pure)]
pub fn get_group_name(group: &mut RhaiGroup) -> String { group.name.clone() }
#[rhai_fn(get = "description", pure)]
pub fn get_group_description(group: &mut RhaiGroup) -> Option<String> { group.description.clone() }
// --- Contact Functions ---
#[rhai_fn(name = "new_contact", return_raw)]
pub fn new_contact() -> Result<RhaiContact, Box<EvalAltResult>> {
let contact = Contact::new();
Ok(contact)
}
/// Sets the contact name
#[rhai_fn(name = "name", return_raw, global, pure)]
pub fn contact_name(contact: &mut RhaiContact, name: String) -> Result<RhaiContact, Box<EvalAltResult>> {
// Create a default Contact to replace the taken one
let default_contact = Contact::new();
// Take ownership of the contact, apply the builder method, then put it back
let owned_contact = std::mem::replace(contact, default_contact);
*contact = owned_contact.name(name);
Ok(contact.clone())
}
/// Sets the contact description
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn contact_description(contact: &mut RhaiContact, description: String) -> Result<RhaiContact, Box<EvalAltResult>> {
// Create a default Contact to replace the taken one
let default_contact = Contact::new();
// Take ownership of the contact, apply the builder method, then put it back
let owned_contact = std::mem::replace(contact, default_contact);
*contact = owned_contact.description(description);
Ok(contact.clone())
}
// Contact Getters
#[rhai_fn(get = "id", pure)]
pub fn get_contact_id(contact: &mut RhaiContact) -> i64 { contact.base_data.id as i64 }
#[rhai_fn(get = "name", pure)]
pub fn get_contact_name(contact: &mut RhaiContact) -> String { contact.name.clone() }
#[rhai_fn(get = "created_at", pure)]
pub fn get_contact_created_at(contact: &mut RhaiContact) -> i64 { contact.base_data.created_at }
#[rhai_fn(get = "modified_at", pure)]
pub fn get_contact_modified_at(contact: &mut RhaiContact) -> i64 { contact.base_data.modified_at }
}
pub fn register_contact_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// Register the exported module globally
let module = exported_module!(rhai_contact_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_group = db.clone();
db_module.set_native_fn("save_group", move |group: Group| -> Result<Group, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_group.set(&group)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_group: {}", e).into(), Position::NONE)))?;
// Return the updated event with the correct ID
Ok(result.1)
});
// Manually register database functions as they need to capture 'db'
let db_clone_delete_group = db.clone();
db_module.set_native_fn("delete_group", move |group: Group| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_group.collection::<Group>()
.expect("can open group collection")
.delete_by_id(group.base_data.id)
.expect("can delete group");
// Return the updated event with the correct ID
Ok(result)
});
let db_clone_get_group = db.clone();
db_module.set_native_fn("get_group_by_id", move |id_i64: INT| -> Result<Group, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_group.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_event_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Event with ID {} not found", id_u32).into(), Position::NONE)))
});
let db_clone_set_contact = db.clone();
db_module.set_native_fn("save_contact", move |contact: Contact| -> Result<Contact, Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_set_contact.set(&contact)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_contact: {}", e).into(), Position::NONE)))?;
// Return the updated contact with the correct ID
Ok(result.1)
});
// Manually register database functions as they need to capture 'db'
let db_clone_delete_contact = db.clone();
db_module.set_native_fn("delete_contact", move |contact: Contact| -> Result<(), Box<EvalAltResult>> {
// Use the Collection trait method directly
let result = db_clone_delete_contact.collection::<Contact>()
.expect("can open contact collection")
.delete_by_id(contact.base_data.id)
.expect("can delete event");
// Return the updated event with the correct ID
Ok(result)
});
let db_clone_get_contact = db.clone();
db_module.set_native_fn("get_contact_by_id", move |id_i64: INT| -> Result<Contact, Box<EvalAltResult>> {
let id_u32 = id_from_i64_to_u32(id_i64)?;
// Use the Collection trait method directly
db_clone_get_contact.get_by_id(id_u32)
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_contact_by_id: {}", e).into(), Position::NONE)))?
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Contact with ID {} not found", id_u32).into(), Position::NONE)))
});
// Add list_contacts function to get all contacts
let db_clone_list_contacts = db.clone();
db_module.set_native_fn("list_contacts", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_contacts.collection::<Contact>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get contact collection: {:?}", e).into(),
Position::NONE
)))?;
let contacts = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all contacts: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for contact in contacts {
array.push(Dynamic::from(contact));
}
Ok(Dynamic::from(array))
});
// Add list_events function to get all events
let db_clone_list_groups = db.clone();
db_module.set_native_fn("list_groups", move || -> Result<Dynamic, Box<EvalAltResult>> {
let collection = db_clone_list_groups.collection::<Group>()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get group collection: {:?}", e).into(),
Position::NONE
)))?;
let groups = collection.get_all()
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get all groups: {:?}", e).into(),
Position::NONE
)))?;
let mut array = Array::new();
for group in groups {
array.push(Dynamic::from(group));
}
Ok(Dynamic::from(array))
});
// Register the database module globally
engine.register_global_module(db_module.into());
println!("Successfully registered contact Rhai module using export_module approach.");
}

View File

@ -2,7 +2,9 @@
pub mod core;
pub mod userexample;
// pub mod productexample; // Temporarily remove as files are missing
pub mod access;
pub mod calendar;
pub mod contact;
pub mod circle;
pub mod governance;
pub mod finance;