Files
osiris/examples/custom_object.rs
Timur Gordon 097360ad12 first commit
2025-10-20 22:24:25 +02:00

296 lines
9.0 KiB
Rust

/// 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<String>,
/// Priority level
#[index]
pub priority: TaskPriority,
/// Current status
#[index]
pub status: TaskStatus,
/// Assigned to user
#[index]
pub assignee: Option<String>,
/// Due date
#[index]
#[serde(with = "time::serde::rfc3339::option")]
pub due_date: Option<OffsetDateTime>,
/// Tags for categorization
#[index]
pub tags: BTreeMap<String, String>,
/// Estimated hours
pub estimated_hours: Option<f32>,
/// Actual hours spent
pub actual_hours: Option<f32>,
}
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<dyn std::error::Error>> {
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(())
}