# Guide: Creating New OSIRIS Objects This guide explains how to properly create new object types in OSIRIS that integrate with the Rhai scripting engine and context storage. ## Step-by-Step Process ### 1. Create the Object Module Create a new file in the appropriate directory under `src/objects/`: - `src/objects/legal/` for legal objects - `src/objects/money/` for financial objects - `src/objects/heroledger/` for HeroLedger objects - etc. ### 2. Define the Object Struct **CRITICAL**: The struct MUST derive `crate::DeriveObject` to automatically implement the `Object` trait. ```rust use crate::store::BaseData; use serde::{Deserialize, Serialize}; /// Your object description #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, crate::DeriveObject)] pub struct YourObject { /// Base data for object storage (REQUIRED) pub base_data: BaseData, /// Your custom fields pub title: String, pub status: YourStatus, // ... other fields } ``` **Required traits:** - `Debug` - for debugging - `Clone` - required by Object trait - `Serialize, Deserialize` - for JSON serialization - `PartialEq` - for comparisons - `crate::DeriveObject` - **CRITICAL** - auto-implements Object trait **Required field:** - `base_data: BaseData` - MUST be present for object storage ### 3. Implement Constructor and Methods ```rust impl YourObject { /// Create a new object pub fn new(id: u32) -> Self { let base_data = BaseData::with_id(id, String::new()); Self { base_data, title: String::new(), status: YourStatus::default(), // ... initialize other fields } } /// Fluent builder methods pub fn title(mut self, title: impl ToString) -> Self { self.title = title.to_string(); self } // ... other methods } ``` ### 4. Create Rhai Bindings Module Create `rhai.rs` in the same directory: ```rust use ::rhai::plugin::*; use ::rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, TypeBuilder}; use super::{YourObject, YourStatus}; /// Register your modules with the Rhai engine pub fn register_your_modules(parent_module: &mut Module) { // Register custom types parent_module.set_custom_type::("YourObject"); parent_module.set_custom_type::("YourStatus"); // Merge functions let your_module = exported_module!(rhai_your_module); parent_module.merge(&your_module); } #[export_module] mod rhai_your_module { use super::YourObject; use ::rhai::EvalAltResult; // Constructor #[rhai_fn(name = "new_your_object", return_raw)] pub fn new_your_object(id: i64) -> Result> { Ok(YourObject::new(id as u32)) } // Builder methods #[rhai_fn(name = "title", return_raw)] pub fn set_title( obj: YourObject, title: String, ) -> Result> { Ok(obj.title(title)) } // Getters #[rhai_fn(name = "title", pure)] pub fn get_title(obj: &mut YourObject) -> String { obj.title.clone() } } // CustomType implementations impl CustomType for YourObject { fn build(mut builder: TypeBuilder) { builder.with_name("YourObject"); } } impl CustomType for YourStatus { fn build(mut builder: TypeBuilder) { builder.with_name("YourStatus"); } } ``` ### 5. Update Module Exports In `mod.rs` of your object category: ```rust pub mod your_object; pub mod rhai; pub use your_object::{YourObject, YourStatus}; pub use rhai::register_your_modules; ``` In `src/objects/mod.rs`: ```rust pub mod your_category; pub use your_category::{YourObject, YourStatus}; ``` ### 6. Register in Engine (CRITICAL STEP) In `src/engine.rs`, add the save registration in the `OsirisPackage` definition: ```rust def_package! { pub OsirisPackage(module) : StandardPackage { // ... existing registrations ... // Add your object's save method FuncRegistration::new("save") .set_into_module(module, |ctx: &mut OsirisContext, obj: crate::objects::YourObject| ctx.save_object(obj)); // ... existing registrations ... // Register your modules register_your_modules(module); } } ``` Also add the import at the top of `engine.rs`: ```rust use crate::objects::your_category::rhai::register_your_modules; ``` ### 7. Create Example Script Create `examples/engine/XX_your_object.rhai`: ```rhai print("=== Your Object Example ===\n"); // Get context let ctx = get_context(["alice", "bob"]); // Create object let obj = new_your_object(1) .title("Example Title"); print("Object created: " + obj.title()); // Store in context ctx.save(obj); print("Object stored"); print("\n=== Example Complete ==="); ``` ## Checklist Before considering your object complete, verify: - [ ] Struct derives `crate::DeriveObject` - [ ] Struct has `base_data: BaseData` field - [ ] Rhai module created with `register_*_modules` function - [ ] Custom types registered with `set_custom_type` - [ ] Module exported in `mod.rs` files - [ ] Save method registered in `src/engine.rs` - [ ] Module registration added to `OsirisPackage` in `src/engine.rs` - [ ] Example script created and tested - [ ] Example runs successfully with `cargo run --example engine examples/engine/XX_your_object.rhai` ## Common Mistakes to Avoid 1. **Forgetting `crate::DeriveObject`** - Without this, the Object trait won't be implemented 2. **Missing `base_data` field** - Required for all storable objects 3. **Not registering save in engine.rs** - The save method MUST be in engine.rs, not context.rs 4. **Not calling `set_custom_type`** - Rhai won't recognize your type 5. **Not merging the exported module** - Your functions won't be available ## Example: Contract Object See the Contract object implementation as a reference: - Struct: `src/objects/legal/contract.rs` - Rhai bindings: `src/objects/legal/rhai.rs` - Module exports: `src/objects/legal/mod.rs` and `src/objects/mod.rs` - Engine registration: `src/engine.rs` (line ~110 and ~138) - Example: `examples/engine/12_contract.rhai`