# OSIRIS Derive Macro The `#[derive(DeriveObject)]` macro automatically implements the `Object` trait for your structs, generating index keys based on fields marked with `#[index]`. ## Usage ```rust use osiris::{BaseData, DeriveObject}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; #[derive(Debug, Clone, Serialize, Deserialize, DeriveObject)] pub struct Note { pub base_data: BaseData, #[index] pub title: Option, pub content: Option, #[index] pub tags: BTreeMap, } ``` ## What Gets Generated The derive macro automatically implements: 1. **`object_type()`** - Returns the struct name as a string 2. **`base_data()`** - Returns a reference to `base_data` 3. **`base_data_mut()`** - Returns a mutable reference to `base_data` 4. **`index_keys()`** - Generates index keys for all `#[index]` fields 5. **`indexed_fields()`** - Returns a list of indexed field names ## Supported Field Types ### Option ```rust #[index] pub title: Option, ``` Generates: `IndexKey { name: "title", value: }` (only if Some) ### BTreeMap ```rust #[index] pub tags: BTreeMap, ``` Generates: `IndexKey { name: "tags:tag", value: "key=value" }` for each entry ### Vec ```rust #[index] pub items: Vec, ``` Generates: `IndexKey { name: "items:item", value: "0:value" }` for each item ### OffsetDateTime ```rust #[index] pub start_time: OffsetDateTime, ``` Generates: `IndexKey { name: "start_time", value: "2025-10-20" }` (date only) ### Enums and Other Types ```rust #[index] pub status: EventStatus, ``` Generates: `IndexKey { name: "status", value: "Debug(status)" }` (using Debug format) ## Complete Example ```rust use osiris::{BaseData, DeriveObject}; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum EventStatus { Draft, Published, Cancelled, } #[derive(Debug, Clone, Serialize, Deserialize, DeriveObject)] pub struct Event { pub base_data: BaseData, #[index] pub title: String, pub description: Option, #[index] #[serde(with = "time::serde::timestamp")] pub start_time: OffsetDateTime, #[index] pub location: Option, #[index] pub status: EventStatus, pub all_day: bool, #[index] pub category: Option, } impl Event { pub fn new(ns: String, title: impl ToString) -> Self { let now = OffsetDateTime::now_utc(); Self { base_data: BaseData::new(ns), title: title.to_string(), description: None, start_time: now, location: None, status: EventStatus::Draft, all_day: false, category: None, } } } ``` ## Generated Index Keys For the Event example above with: - `title = "Team Meeting"` - `start_time = 2025-10-20T10:00:00Z` - `location = Some("Room 101")` - `status = EventStatus::Published` - `category = Some("work")` The generated index keys would be: ```rust vec![ IndexKey { name: "mime", value: "application/json" }, // from base_data IndexKey { name: "title", value: "Team Meeting" }, IndexKey { name: "start_time", value: "2025-10-20" }, IndexKey { name: "location", value: "Room 101" }, IndexKey { name: "status", value: "Published" }, IndexKey { name: "category", value: "work" }, ] ``` ## HeroDB Storage These index keys are stored in HeroDB as: ``` idx:events:title:Team Meeting → {event_id} idx:events:start_time:2025-10-20 → {event_id} idx:events:location:Room 101 → {event_id} idx:events:status:Published → {event_id} idx:events:category:work → {event_id} ``` ## Querying by Index ```rust use osiris::store::GenericStore; let store = GenericStore::new(client); // Get all events on a specific date let ids = store.get_ids_by_index("events", "start_time", "2025-10-20").await?; // Get all published events let ids = store.get_ids_by_index("events", "status", "Published").await?; // Get all events in a category let ids = store.get_ids_by_index("events", "category", "work").await?; ``` ## Requirements 1. **Must have `base_data` field**: The struct must have a field named `base_data` of type `BaseData` 2. **Must derive standard traits**: `Debug`, `Clone`, `Serialize`, `Deserialize` 3. **Fields marked with `#[index]`**: Only fields with the `#[index]` attribute will be indexed ## Limitations - The macro currently uses `Debug` formatting for enums and complex types - BTreeMap indexing assumes `String` keys and values - Vec indexing uses numeric indices (may not be ideal for all use cases) ## Future Enhancements - Custom index key formatters via attributes - Support for nested struct indexing - Conditional indexing (e.g., `#[index(if = "is_published")]`) - Custom index names (e.g., `#[index(name = "custom_name")]`)