#[cfg(feature = "sqlx-storage")]
use a2a_rs::adapter::storage::{DatabaseConfig, SqlxTaskStorage};
#[cfg(feature = "sqlx-storage")]
use a2a_rs::domain::TaskState;
#[cfg(feature = "sqlx-storage")]
use a2a_rs::port::AsyncTaskLifecycle;
#[cfg(feature = "sqlx-storage")]
fn tid(s: &str) -> a2a_rs::domain::TaskId {
s.parse().unwrap()
}
#[cfg(feature = "sqlx-storage")]
fn cid(s: &str) -> a2a_rs::domain::ContextId {
s.parse().unwrap()
}
#[cfg(feature = "sqlx-storage")]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
println!("🗃️ SQLx Storage Demo");
println!("=====================");
let config = match DatabaseConfig::from_env() {
Ok(config) => {
println!("📡 Using database configuration from environment");
config
}
Err(_) => {
println!("📋 Using default configuration (SQLite in-memory)");
DatabaseConfig::default()
}
};
config
.validate()
.map_err(|e| format!("Invalid database configuration: {}", e))?;
println!("🔧 Database type: {:?}", config.database_type());
println!("🔗 Database URL: {}", config.url);
println!("📊 Max connections: {}", config.max_connections);
println!();
println!("🚀 Initializing SQLx storage...");
let storage = SqlxTaskStorage::new(&config.url).await?;
println!("✅ Storage initialized successfully");
println!();
println!("📝 Creating tasks...");
let task_ids = vec!["demo-task-1", "demo-task-2", "demo-task-3"];
for task_id in &task_ids {
let task = storage.create(&tid(task_id), &cid("demo-context")).await?;
println!(
" ✓ Created task: {} (status: {:?})",
task.id, task.status.state
);
}
println!();
println!("🔄 Updating task statuses...");
storage
.update_status(&tid("demo-task-1"), TaskState::Working, None)
.await?;
println!(" ✓ Updated demo-task-1 to Working");
storage
.update_status(&tid("demo-task-2"), TaskState::Working, None)
.await?;
storage
.update_status(&tid("demo-task-2"), TaskState::Completed, None)
.await?;
println!(" ✓ Updated demo-task-2 to Working, then Completed");
storage
.update_status(&tid("demo-task-3"), TaskState::Working, None)
.await?;
println!(" ✓ Updated demo-task-3 to Working");
println!();
println!("❌ Canceling a task...");
let canceled_task = storage.cancel(&tid("demo-task-3")).await?;
println!(
" ✓ Canceled task: {} (status: {:?})",
canceled_task.id, canceled_task.status.state
);
println!();
println!("📖 Retrieving tasks with history...");
for task_id in &task_ids {
let task = storage.get(&tid(task_id), Some(10)).await?;
println!(" 📋 Task: {} (status: {:?})", task.id, task.status.state);
let history = &task.history;
if !history.is_empty() {
println!(" History entries: {}", history.len());
for (i, message) in history.iter().enumerate() {
println!(" {}. Message ID: {}", i + 1, message.message_id);
}
} else {
println!(" No history available");
}
println!();
}
println!("🔍 Checking task existence...");
for task_id in &task_ids {
let exists = storage.exists(&tid(task_id)).await?;
println!(" {} exists: {}", task_id, exists);
}
let exists = storage.exists(&tid("non-existent-task")).await?;
println!(" non-existent-task exists: {}", exists);
println!();
println!("📚 Available configuration examples:");
let examples = DatabaseConfig::examples();
for (name, example_config) in examples {
println!(" {} -> {}", name, example_config.url);
}
println!();
println!("✅ Demo completed successfully!");
println!();
println!("💡 Tips:");
println!(" - Set DATABASE_URL environment variable to use a different database");
println!(" - Use 'sqlite:tasks.db' for persistent SQLite storage");
println!(" - Use 'postgres://user:pass@host/db' for PostgreSQL");
println!(" - Task history is automatically tracked in the database");
println!(" - The storage layer handles migrations automatically");
Ok(())
}
#[cfg(not(feature = "sqlx-storage"))]
fn main() {
eprintln!("❌ This example requires the 'sqlx-storage' feature.");
eprintln!("Run with: cargo run --example sqlx_storage_demo --features sqlite");
std::process::exit(1);
}