/// Custom Object Example /// /// This example demonstrates how to create your own custom object types /// using the derive macro. /// /// Run with: /// ```bash /// cargo run --example custom_object /// ``` use osiris::store::{BaseData, GenericStore, HeroDbClient}; use osiris::{DeriveObject, Object}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use time::OffsetDateTime; // ======================================== // Custom Object: Task // ======================================== #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum TaskPriority { Low, Medium, High, Critical, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum TaskStatus { Todo, InProgress, Done, Blocked, } #[derive(Debug, Clone, Serialize, Deserialize, DeriveObject)] pub struct Task { pub base_data: BaseData, /// Task title #[index] pub title: String, /// Task description pub description: Option, /// Priority level #[index] pub priority: TaskPriority, /// Current status #[index] pub status: TaskStatus, /// Assigned to user #[index] pub assignee: Option, /// Due date #[index] #[serde(with = "time::serde::rfc3339::option")] pub due_date: Option, /// Tags for categorization #[index] pub tags: BTreeMap, /// Estimated hours pub estimated_hours: Option, /// Actual hours spent pub actual_hours: Option, } impl Task { pub fn new(ns: String, title: impl ToString) -> Self { Self { base_data: BaseData::new(ns), title: title.to_string(), description: None, priority: TaskPriority::Medium, status: TaskStatus::Todo, assignee: None, due_date: None, tags: BTreeMap::new(), estimated_hours: None, actual_hours: None, } } pub fn set_description(mut self, description: impl ToString) -> Self { self.description = Some(description.to_string()); self } pub fn set_priority(mut self, priority: TaskPriority) -> Self { self.priority = priority; self } pub fn set_status(mut self, status: TaskStatus) -> Self { self.status = status; self } pub fn set_assignee(mut self, assignee: impl ToString) -> Self { self.assignee = Some(assignee.to_string()); self } pub fn set_due_date(mut self, due_date: OffsetDateTime) -> Self { self.due_date = Some(due_date); self } pub fn add_tag(mut self, key: impl ToString, value: impl ToString) -> Self { self.tags.insert(key.to_string(), value.to_string()); self } pub fn set_estimated_hours(mut self, hours: f32) -> Self { self.estimated_hours = Some(hours); self } } // ======================================== // Main Example // ======================================== #[tokio::main] async fn main() -> Result<(), Box> { println!("๐ŸŽฏ OSIRIS Custom Object Example\n"); // Connect to HeroDB println!("๐Ÿ“ก Connecting to HeroDB..."); let client = HeroDbClient::new("redis://localhost:6379", 2)?; let store = GenericStore::new(client); println!("โœ“ Connected to HeroDB (DB 2)\n"); // ======================================== // Create Tasks // ======================================== println!("๐Ÿ“‹ Creating Tasks"); println!("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"); let now = OffsetDateTime::now_utc(); let tomorrow = now + time::Duration::days(1); let next_week = now + time::Duration::days(7); // Task 1: High priority, assigned let task1 = Task::new("tasks".to_string(), "Implement derive macro") .set_description("Create proc macro for automatic Object trait implementation") .set_priority(TaskPriority::High) .set_status(TaskStatus::Done) .set_assignee("alice") .set_due_date(tomorrow) .add_tag("component", "derive") .add_tag("project", "osiris") .set_estimated_hours(8.0); println!("Task 1: {}", task1.title); println!(" Priority: {:?}", task1.priority); println!(" Status: {:?}", task1.status); println!(" Assignee: {}", task1.assignee.as_ref().unwrap()); store.put(&task1).await?; println!("โœ“ Stored\n"); // Task 2: Critical priority, blocked let task2 = Task::new("tasks".to_string(), "Fix indexing bug") .set_description("BTreeMap indexing has lifetime issues") .set_priority(TaskPriority::Critical) .set_status(TaskStatus::Blocked) .set_assignee("bob") .set_due_date(now) .add_tag("type", "bug") .add_tag("project", "osiris") .set_estimated_hours(4.0); println!("Task 2: {}", task2.title); println!(" Priority: {:?}", task2.priority); println!(" Status: {:?}", task2.status); store.put(&task2).await?; println!("โœ“ Stored\n"); // Task 3: In progress let task3 = Task::new("tasks".to_string(), "Write documentation") .set_description("Document the derive macro usage") .set_priority(TaskPriority::Medium) .set_status(TaskStatus::InProgress) .set_assignee("alice") .set_due_date(next_week) .add_tag("type", "docs") .add_tag("project", "osiris") .set_estimated_hours(6.0); println!("Task 3: {}", task3.title); println!(" Priority: {:?}", task3.priority); println!(" Status: {:?}", task3.status); store.put(&task3).await?; println!("โœ“ Stored\n"); // ======================================== // Query Tasks // ======================================== println!("๐Ÿ” Querying Tasks"); println!("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"); // Query by assignee println!("Tasks assigned to Alice:"); let alice_tasks = store.get_ids_by_index("tasks", "assignee", "alice").await?; for id in &alice_tasks { let task: Task = store.get("tasks", id).await?; println!(" - {} ({:?})", task.title, task.status); } println!(); // Query by priority println!("High priority tasks:"); let high_priority = store.get_ids_by_index("tasks", "priority", "High").await?; for id in &high_priority { let task: Task = store.get("tasks", id).await?; println!(" - {} (assigned to: {})", task.title, task.assignee.as_ref().unwrap_or(&"unassigned".to_string()) ); } println!(); // Query by status println!("Blocked tasks:"); let blocked = store.get_ids_by_index("tasks", "status", "Blocked").await?; for id in &blocked { let task: Task = store.get("tasks", id).await?; println!(" - {} (priority: {:?})", task.title, task.priority); } println!(); // Query by tag println!("Tasks tagged with project=osiris:"); let project_tasks = store.get_ids_by_index("tasks", "tags:tag", "project=osiris").await?; println!(" Found {} tasks", project_tasks.len()); println!(); // ======================================== // Show Auto-Generated Indexes // ======================================== println!("๐Ÿ“Š Auto-Generated Indexes"); println!("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"); println!("Task indexed fields: {:?}", Task::indexed_fields()); println!(); println!("Index keys for '{}':", task1.title); for key in task1.index_keys() { println!(" - {} = {}", key.name, key.value); } println!(); // ======================================== // Update Task Status // ======================================== println!("โœ๏ธ Updating Task Status"); println!("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"); // Retrieve, modify, and store let mut task2_updated: Task = store.get("tasks", &task2.base_data.id).await?; println!("Updating '{}' status from {:?} to {:?}", task2_updated.title, task2_updated.status, TaskStatus::InProgress ); task2_updated.status = TaskStatus::InProgress; task2_updated.base_data.update_modified(); store.put(&task2_updated).await?; println!("โœ“ Task updated\n"); // ======================================== // Cleanup // ======================================== println!("๐Ÿงน Cleanup"); println!("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"); store.delete(&task1).await?; store.delete(&task2_updated).await?; store.delete(&task3).await?; println!("โœ“ All tasks deleted\n"); println!("โœ… Example completed successfully!"); Ok(()) }