9.2 KiB
AI Prompt: Generate rhai.rs
for a new Rust Model
Objective:
Create a rhai.rs
file to expose the Rust model [YourModelName]
(and any related owned sub-models like [YourSubModelName]
) to the Rhai scripting engine. This file should allow Rhai scripts to create, manipulate, and retrieve instances of [YourModelName]
.
Input Requirements (Provide this information for your specific model):
-
Rust Struct Definition(s):
// Example for [YourModelName] #[derive(Clone, Debug, Serialize, Deserialize)] // Ensure necessary derives pub struct [YourModelName] { pub base_data: BaseModelData, // Common field for ID pub unique_id_field: String, // Example: like flow_uuid pub name: String, pub status: String, // If it owns a collection of sub-models: pub sub_items: Vec<[YourSubModelName]>, // Other fields... } impl [YourModelName] { // Constructor pub fn new(id: u32, unique_id_field: String /*, other essential params */) -> Self { Self { base_data: BaseModelData::new(id), unique_id_field, name: "".to_string(), // Default or passed in status: "".to_string(), // Default or passed in sub_items: Vec::new(), // ... } } // Builder methods pub fn name(mut self, name: String) -> Self { self.name = name; self } pub fn status(mut self, status: String) -> Self { self.status = status; self } // Method to add sub-items pub fn add_sub_item(mut self, item: [YourSubModelName]) -> Self { self.sub_items.push(item); self } // Other methods to expose... } // Example for [YourSubModelName] (if applicable) #[derive(Clone, Debug, Serialize, Deserialize)] pub struct [YourSubModelName] { pub id: u32, pub description: String, // ... } impl [YourSubModelName] { pub fn new(id: u32, description: String) -> Self { Self { id, description } } // Builder methods for sub-model... }
-
Key ID fields that need
i64
(Rhai) tou32
(Rust) conversion: (e.g.,base_data.id
,[YourSubModelName].id
, any foreign key IDs).
Implementation Guidelines for rhai.rs
:
-
File Structure:
- Start with necessary imports:
rhai::{Dynamic, Engine, EvalAltResult, NativeCallContext, Position}
,std::sync::Arc
, your model structs,BaseModelData
,OurDB
. - Include the
i64_to_u32
helper function. - Define
pub fn register_[your_model_name]_rhai_module(engine: &mut Engine, db: Arc<OurDB>) { ... }
.
- Start with necessary imports:
-
Helper Function for ID Conversion:
fn i64_to_u32(val: i64, context_pos: Position, field_name: &str, object_name: &str) -> Result<u32, Box<EvalAltResult>> { val.try_into().map_err(|_e| { Box::new(EvalAltResult::ErrorArithmetic( format!("Conversion error for {} in {} from i64 to u32", field_name, object_name), context_pos, )) }) }
-
Constructors (e.g.,
new_[your_model_name]
):- Use
NativeCallContext
for manual argument parsing andi64_to_u32
conversion for ID fields. - Example:
engine.register_fn("new_[your_model_name]", move |context: NativeCallContext, id_i64: i64, unique_id_str: String /*, other_args... */| -> Result<[YourModelName], Box<EvalAltResult>> { let id_u32 = i64_to_u32(id_i64, context.position(), "id", "new_[your_model_name]")?; Ok([YourModelName]::new(id_u32, unique_id_str /*, ... */)) });
- Do the same for
new_[your_sub_model_name]
if applicable. - Note on
adapter_macros
:adapt_rhai_i64_input_fn!
is generally NOT suitable for constructors with multiple arguments or mixed types (e.g.,u32
andString
). PreferNativeCallContext
.
- Use
-
Builder Methods:
- Register functions that take ownership of the model, modify it, and return it.
- Example:
engine.register_fn("name", |model: [YourModelName], name_val: String| -> [YourModelName] { model.name(name_val) }); engine.register_fn("status", |model: [YourModelName], status_val: String| -> [YourModelName] { model.status(status_val) }); // For adding sub-items (if applicable) engine.register_fn("add_sub_item", |model: [YourModelName], item: [YourSubModelName]| -> [YourModelName] { model.add_sub_item(item) });
-
Getters:
- Use
engine.register_get("field_name", |model: &mut [YourModelName]| -> Result<FieldType, Box<EvalAltResult>> { Ok(model.field.clone()) });
- For
base_data.id
(u32), cast toi64
for Rhai:Ok(model.base_data.id as i64)
- For
Vec<[YourSubModelName]>
fields (e.g.,sub_items
): Convert torhai::Array
.
(Ensureengine.register_get("sub_items", |model: &mut [YourModelName]| -> Result<rhai::Array, Box<EvalAltResult>> { let rhai_array = model.sub_items.iter().cloned().map(Dynamic::from).collect::<rhai::Array>(); Ok(rhai_array) });
[YourSubModelName]
isClone
and works withDynamic::from
).
- Use
-
Setters (Less common if using a full builder pattern, but can be useful):
- Use
engine.register_set("field_name", |model: &mut [YourModelName], value: FieldType| { model.field = value; Ok(()) });
- If a setter method in Rust takes an ID (e.g.,
set_related_id(id: u32)
), and you want to call it from Rhai with ani64
:- You could use
adapter_macros::adapt_rhai_i64_input_method!(YourModelType::set_related_id, u32)
ifYourModelType::set_related_id
takes&mut self, u32
. - Or, handle manually with
NativeCallContext
if the method signature is more complex or doesn't fit the macro.
- You could use
- Use
-
Other Custom Methods:
- Register any other public methods from your Rust struct that should be callable.
- Example:
engine.register_fn("custom_method_name", |model: &mut [YourModelName]| model.custom_method_in_rust());
-
Database Interaction (Using actual
OurDB
methods likeset
andget_by_id
):-
The
Arc<OurDB>
instance passed toregister_[your_model_name]_rhai_module
should be cloned and captured by the closures for DB functions. -
The Rhai script will call these functions without explicitly passing the DB instance (e.g.,
set_my_model(my_model_instance);
,let m = get_my_model_by_id(123);
). -
Ensure proper error handling, converting DB errors and
Option::None
(for getters) toBox<EvalAltResult::ErrorRuntime(...)
. -
Example for
set_[your_model_name]
:// In register_[your_model_name]_rhai_module(engine: &mut Engine, db: Arc<OurDB>) let captured_db_for_set = Arc::clone(&db); engine.register_fn("set_[your_model_name]", move |model: [YourModelName]| -> Result<(), Box<EvalAltResult>> { captured_db_for_set.set(&model).map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Failed to set [YourModelName] (ID: {}): {}", model.base_data.id, e).into(), Position::NONE, )) }) });
-
Example for
get_[your_model_name]_by_id
:// In register_[your_model_name]_rhai_module(engine: &mut Engine, db: Arc<OurDB>) let captured_db_for_get = Arc::clone(&db); engine.register_fn("get_[your_model_name]_by_id", move |context: NativeCallContext, id_i64: i64| -> Result<[YourModelName], Box<EvalAltResult>> { let id_u32 = i64_to_u32(id_i64, context.position(), "id", "get_[your_model_name]_by_id")?; captured_db_for_get.get_by_id(id_u32) // Assumes OurDB directly implements Collection<_, [YourModelName]> .map_err(|e| Box::new(EvalAltResult::ErrorRuntime( format!("Error getting [YourModelName] (ID: {}): {}", id_u32, e).into(), Position::NONE, )))? .ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime( format!("[YourModelName] with ID {} not found", id_u32).into(), Position::NONE, ))) });
-
Example Output Snippet (Illustrating a few parts):
// #[...]
// pub struct MyItem { /* ... */ }
// impl MyItem { /* ... */ }
// pub fn register_my_item_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
// fn i64_to_u32(...) { /* ... */ }
//
// engine.register_fn("new_my_item", |ctx: NativeCallContext, id: i64, name: String| { /* ... */ });
// engine.register_fn("name", |item: MyItem, name: String| -> MyItem { /* ... */ });
// engine.register_get("id", |item: &mut MyItem| Ok(item.base_data.id as i64));
// engine.register_get("sub_elements", |item: &mut MyItem| {
// Ok(item.sub_elements.iter().cloned().map(Dynamic::from).collect::<rhai::Array>())
// });
// // ... etc.
// }