Files
osiris/TYPE_REGISTRY_DESIGN.md
Timur Gordon e04012c8c0 Refactor Rhai integration with context-based execution and type registry
Major Changes:
- Moved Rhai support from rhai_support/ to rhai/ module
- Implemented context-based execution with signatory access control
- Added TypeRegistry for dynamic type registration and object creation
- Refactored engine to use context (Vec<String>) instead of instance
- Removed old runner binary (moved to runner_rust crate)

Rhai Module:
- engine.rs: Core Rhai engine with context-based get_context()
- functions.rs: Rhai function bindings (create_note, create_event, etc.)
- mod.rs: Module exports and organization

Store Improvements:
- TypeRegistry for registering object types and creators
- Generic store uses type registry for dynamic object creation
- Improved error handling and type safety

Documentation:
- RHAI_REFACTOR_COMPLETE.md: Refactoring details
- SIGNATORY_ACCESS_CONTROL.md: Context-based access control
- TYPE_REGISTRY_DESIGN.md: Type registry architecture
- REFACTORING_COMPLETE.md: Overall refactoring summary
- TESTS_COMPLETE.md: Testing documentation

Build Status:  Compiles successfully with minor warnings
2025-10-28 03:33:39 +01:00

2.7 KiB

OSIRIS Type Registry Design

Problem

We need applications (like ZDFZ API) to register custom types with OSIRIS so that:

  1. The save() method can use the correct struct type instead of hardcoding Note
  2. Each collection name maps to a specific Rust type
  3. The type system properly deserializes, indexes, and stores data

Challenge

The Object trait is not "dyn compatible" (object-safe) because it has:

  • Associated functions (object_type(), from_json())
  • Generic methods
  • Serialize/Deserialize bounds

This means we cannot use Box<dyn Object> for dynamic dispatch.

Solution: Type Registry with Callbacks

Instead of trying to return Box<dyn Object>, we use a callback-based approach:

pub struct TypeRegistry {
    // For each collection, store a function that:
    // 1. Takes JSON string
    // 2. Deserializes to the correct type
    // 3. Stores it using GenericStore
    // 4. Returns the ID
    savers: HashMap<String, Box<dyn Fn(&GenericStore, &str, &str) -> Result<()>>>,
}

Usage in ZDFZ API:

// Create registry
let registry = TypeRegistry::new();

// Register Resident type
registry.register_saver("residents", |store, id, json| {
    let mut resident: Resident = serde_json::from_str(json)?;
    resident.set_id(id);
    store.put(&resident).await
});

// Register Company type  
registry.register_saver("companies", |store, id, json| {
    let mut company: Company = serde_json::from_str(json)?;
    company.set_id(id);
    store.put(&company).await
});

// Create OSIRIS context with registry
let ctx = OsirisContext::new_with_registry(
    "my_context",
    "owner_id", 
    herodb_url,
    db_id,
    Some(Arc::new(registry))
);

// Now save() uses the registered type!
ctx.save("residents", "id123", resident_json)?;

Benefits:

Type-safe - Each collection uses its proper Rust type Flexible - Applications register their own types No trait object issues - Uses closures instead of Box<dyn Object> Proper indexing - Each type's index_keys() method is called Clean API - Simple registration interface

Implementation Plan:

  1. Create TypeRegistry with callback-based savers
  2. Add set_registry() to GenericStore
  3. Update OsirisContext::save() to use registry if available
  4. Fall back to Note if no registry or collection not registered
  5. Document usage for ZDFZ API

Next Steps:

The type registry infrastructure is in place. Now ZDFZ API can:

  1. Create a TypeRegistry
  2. Register all SDK model types
  3. Pass registry when creating OSIRIS contexts
  4. Use generic save() method with proper types!

Status: Design complete, ready for implementation with callback approach.