296 lines
9.0 KiB
Rust
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(())
|
|
}
|