use observable_property_tokio::{ObservableProperty, PropertyError};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Observable Property Error Diagnostics Example ===\n");
let property = ObservableProperty::new(0);
println!("1. Error Helper Functions:");
let read_error = PropertyError::read_lock_error("get_value", "failed to acquire read lock");
println!(" Error: {}", read_error);
println!(" Diagnostic: {}\n", read_error.diagnostic_info());
println!("2. Capacity Exceeded Error:");
let capacity_error = PropertyError::CapacityExceeded {
current: 150,
max: 100,
resource: "observers".to_string(),
};
println!(" Error: {}", capacity_error);
println!(" Diagnostic: {}\n", capacity_error.diagnostic_info());
println!("3. Operation Timeout Error:");
let timeout_error = PropertyError::OperationTimeout {
operation: "notify_observers".to_string(),
elapsed_ms: 5500,
threshold_ms: 5000,
};
println!(" Error: {}", timeout_error);
println!(" Diagnostic: {}\n", timeout_error.diagnostic_info());
println!("4. Observer Panic Error:");
let panic_error = PropertyError::observer_panic(
observable_property_tokio::ObserverId::from(42),
"observer function panicked with: division by zero"
);
println!(" Error: {}", panic_error);
println!(" Diagnostic: {}\n", panic_error.diagnostic_info());
println!("5. Lock Poisoned Error:");
let poisoned_error = PropertyError::lock_poisoned(
"set_value",
"inner property lock was poisoned by previous panic"
);
println!(" Error: {}", poisoned_error);
println!(" Diagnostic: {}\n", poisoned_error.diagnostic_info());
println!("6. Practical Usage with Error Handling:");
let counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));
let counter_clone = counter.clone();
property.subscribe(Arc::new(move |old, new| {
println!(" Observer triggered: {} -> {}", old, new);
counter_clone.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}))?;
match property.set(42) {
Ok(_) => println!(" ✓ Successfully set value to 42"),
Err(e) => {
eprintln!(" ✗ Error occurred: {}", e);
eprintln!(" Diagnostic info: {}", e.diagnostic_info());
}
}
println!("\n7. Observer Not Found Error:");
let fake_id = observable_property_tokio::ObserverId::from(999);
match property.unsubscribe(fake_id) {
Ok(_) => println!(" Observer removed"),
Err(e) => {
println!(" Expected error: {}", e);
println!(" Diagnostic: {}", e.diagnostic_info());
}
}
println!("\n8. Shutdown In Progress Error:");
let shutdown_error = PropertyError::ShutdownInProgress;
println!(" Error: {}", shutdown_error);
println!(" Diagnostic: {}\n", shutdown_error.diagnostic_info());
println!("=== Integration with Logging ===");
println!("In production, you would typically use the diagnostic_info() method like this:");
println!(r#"
match property.set(value) {{
Ok(_) => {{ /* success */ }}
Err(e) => {{
// Log to your monitoring system
log::error!("Property operation failed: {{}}", e.diagnostic_info());
// Or send to a metrics system
metrics::increment_counter!("property_errors", "type" => error_type(&e));
// Or include in structured logging
tracing::error!(
diagnostic = %e.diagnostic_info(),
error = %e,
"Property operation failed"
);
}}
}}
"#);
println!("\n=== Benefits of Diagnostic Information ===");
println!("• Timestamps help correlate errors with other system events");
println!("• Operation context shows exactly what was being attempted");
println!("• Resource metrics (like utilization %) aid in capacity planning");
println!("• Structured format makes errors easy to parse and aggregate");
println!("• Performance metrics help identify bottlenecks and SLA violations");
Ok(())
}