#![expect(unused_crate_dependencies)]
use serde::{Deserialize, Serialize};
use stately::prelude::*;
#[stately::entity]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Pipeline {
pub name: String,
pub description: Option<String>,
}
#[stately::entity]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Source {
pub name: String,
pub url: String,
}
#[stately::entity]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Sink {
pub name: String,
pub destination: String,
}
#[stately::entity(singleton, description = "Global configuration")]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Config {
pub max_connections: usize,
pub timeout_seconds: u64,
}
#[stately::entity]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Task {
pub name: String,
pub status: String,
}
#[stately::entity]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Job {
pub name: String,
pub priority: u32,
}
type TaskCache = Collection<Task>;
#[stately::state]
pub struct AppState {
#[singleton]
pub config: Config,
pub pipelines: Pipeline,
#[collection(variant = "ExplicitSource")]
pub sources: Source,
#[collection(TaskCache, variant = "CachedTask")]
pub tasks: Task,
#[collection(variant = "ArchivedPipeline")]
pub archived: Pipeline,
#[collection(TaskCache, variant = "BackgroundTask")]
pub background: Task,
pub sinks: Sink,
pub jobs: Job,
}
fn main() {
println!("=== Stately Basic Example ===\n");
println!("Demonstrating all 5 collection syntax permutations:\n");
let mut state = AppState::new();
state.config.set(Config { max_connections: 100, timeout_seconds: 30 });
println!("✓ Configured singleton (Config)");
let pipeline_id = state.pipelines.create(Pipeline {
name: "data-pipeline".to_string(),
description: Some("Main data processing pipeline".to_string()),
});
println!("✓ Case 1 - Implicit collection: Created pipeline {pipeline_id}");
let source_id = state.sources.create(Source {
name: "api-source".to_string(),
url: "http://example.com/data".to_string(),
});
println!("✓ Case 2 - Explicit with variant: Created source {source_id}");
let task_id = state
.tasks
.create(Task { name: "process-data".to_string(), status: "pending".to_string() });
println!("✓ Case 3 - Custom type: Created task {task_id}");
let archived_id = state.archived.create(ArchivedPipeline(Pipeline {
name: "old-pipeline".to_string(),
description: Some("Archived for reference".to_string()),
}));
println!("✓ Case 4 - Variant override: Created archived pipeline {archived_id}");
let background_id = state.background.create(BackgroundTask(Task {
name: "cleanup".to_string(),
status: "running".to_string(),
}));
println!("✓ Case 5 - Custom + variant: Created background task {background_id}");
let sink_id = state.sinks.create(Sink {
name: "s3-sink".to_string(),
destination: "s3://my-bucket/output".to_string(),
});
println!("✓ Created sink {sink_id}");
println!("\n=== Entity Summaries ===");
let summaries = state.list_entities(None);
for (entry, summaries) in summaries {
println!("\n{}:", entry.as_ref());
for summary in summaries {
println!(" - {} ({})", summary.name, summary.id);
if let Some(desc) = summary.description {
println!(" Description: {desc}");
}
}
}
println!("\n=== Search Results for 'pipeline' ===");
let search_results = state.search_entities("pipeline");
for (entry, entities) in search_results {
println!("\n{}:", entry.as_ref());
for (id, _entity) in entities {
println!(" - {id}");
}
}
println!("\n=== Get Specific Entities ===");
if let Some((id, _entity)) = state.get_entity(&pipeline_id, StateEntry::Pipeline) {
println!("Found Pipeline: {id}");
}
if let Some((id, _entity)) = state.get_entity(&source_id, StateEntry::ExplicitSource) {
println!("Found ExplicitSource: {id}");
}
if let Some((id, _entity)) = state.get_entity(&task_id, StateEntry::CachedTask) {
println!("Found CachedTask: {id}");
}
if let Some((id, _entity)) = state.get_entity(&archived_id, StateEntry::ArchivedPipeline) {
println!("Found ArchivedPipeline: {id}");
}
if let Some((id, _entity)) = state.get_entity(&background_id, StateEntry::BackgroundTask) {
println!("Found BackgroundTask: {id}");
}
println!("\n=== Update Entity ===");
let updated_source =
Source { name: "api-source".to_string(), url: "http://example.com/new-data".to_string() };
match state.sources.update(&source_id, updated_source) {
Ok(()) => println!("Updated source"),
Err(e) => println!("Error updating source: {e}"),
}
println!("\n=== Serialization ===");
let json = serde_json::to_string_pretty(&state.sources).unwrap();
println!("Sources as JSON:\n{json}");
println!("\n=== Example Complete ===");
}