193 lines
9.2 KiB
Markdown
193 lines
9.2 KiB
Markdown
## 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):**
|
|
|
|
1. **Rust Struct Definition(s):**
|
|
```rust
|
|
// 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...
|
|
}
|
|
```
|
|
|
|
2. **Key ID fields that need `i64` (Rhai) to `u32` (Rust) conversion:** (e.g., `base_data.id`, `[YourSubModelName].id`, any foreign key IDs).
|
|
|
|
**Implementation Guidelines for `rhai.rs`:**
|
|
|
|
1. **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>) { ... }`.
|
|
|
|
2. **Helper Function for ID Conversion:**
|
|
```rust
|
|
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,
|
|
))
|
|
})
|
|
}
|
|
```
|
|
|
|
3. **Constructors (e.g., `new_[your_model_name]`):**
|
|
* Use `NativeCallContext` for manual argument parsing and `i64_to_u32` conversion for ID fields.
|
|
* Example:
|
|
```rust
|
|
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` and `String`). Prefer `NativeCallContext`.
|
|
|
|
4. **Builder Methods:**
|
|
* Register functions that take ownership of the model, modify it, and return it.
|
|
* Example:
|
|
```rust
|
|
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) });
|
|
```
|
|
|
|
5. **Getters:**
|
|
* Use `engine.register_get("field_name", |model: &mut [YourModelName]| -> Result<FieldType, Box<EvalAltResult>> { Ok(model.field.clone()) });`
|
|
* For `base_data.id` (u32), cast to `i64` for Rhai: `Ok(model.base_data.id as i64)`
|
|
* **For `Vec<[YourSubModelName]>` fields (e.g., `sub_items`):** Convert to `rhai::Array`.
|
|
```rust
|
|
engine.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)
|
|
});
|
|
```
|
|
(Ensure `[YourSubModelName]` is `Clone` and works with `Dynamic::from`).
|
|
|
|
6. **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 an `i64`:
|
|
* You *could* use `adapter_macros::adapt_rhai_i64_input_method!(YourModelType::set_related_id, u32)` if `YourModelType::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.
|
|
|
|
7. **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());`
|
|
|
|
8. **Database Interaction (Using actual `OurDB` methods like `set` and `get_by_id`):**
|
|
* The `Arc<OurDB>` instance passed to `register_[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) to `Box<EvalAltResult::ErrorRuntime(...)`.
|
|
|
|
* **Example for `set_[your_model_name]`:**
|
|
```rust
|
|
// 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`:**
|
|
```rust
|
|
// 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):**
|
|
|
|
```rust
|
|
// #[...]
|
|
// 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.
|
|
// }
|
|
```
|