196 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# 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<String>,
 | 
						|
    
 | 
						|
    pub content: Option<String>,
 | 
						|
    
 | 
						|
    #[index]
 | 
						|
    pub tags: BTreeMap<String, String>,
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
## 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<T>
 | 
						|
```rust
 | 
						|
#[index]
 | 
						|
pub title: Option<String>,
 | 
						|
```
 | 
						|
Generates: `IndexKey { name: "title", value: <string_value> }` (only if Some)
 | 
						|
 | 
						|
### BTreeMap<String, String>
 | 
						|
```rust
 | 
						|
#[index]
 | 
						|
pub tags: BTreeMap<String, String>,
 | 
						|
```
 | 
						|
Generates: `IndexKey { name: "tags:tag", value: "key=value" }` for each entry
 | 
						|
 | 
						|
### Vec<T>
 | 
						|
```rust
 | 
						|
#[index]
 | 
						|
pub items: Vec<String>,
 | 
						|
```
 | 
						|
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<String>,
 | 
						|
    
 | 
						|
    #[index]
 | 
						|
    #[serde(with = "time::serde::timestamp")]
 | 
						|
    pub start_time: OffsetDateTime,
 | 
						|
    
 | 
						|
    #[index]
 | 
						|
    pub location: Option<String>,
 | 
						|
    
 | 
						|
    #[index]
 | 
						|
    pub status: EventStatus,
 | 
						|
    
 | 
						|
    pub all_day: bool,
 | 
						|
    
 | 
						|
    #[index]
 | 
						|
    pub category: Option<String>,
 | 
						|
}
 | 
						|
 | 
						|
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")]`)
 |