177 lines
5.5 KiB
Rust
177 lines
5.5 KiB
Rust
|
|
//! # Error Handling Example
|
||
|
|
//!
|
||
|
|
//! Demonstrates how to properly handle and propagate errors in VAPORA.
|
||
|
|
//!
|
||
|
|
//! ## What This Example Shows
|
||
|
|
//! - Using VAPORA's error types
|
||
|
|
//! - Adding context to errors
|
||
|
|
//! - Error propagation with the `?` operator
|
||
|
|
//! - Error display and debugging
|
||
|
|
//! - Common error scenarios
|
||
|
|
//!
|
||
|
|
//! ## Run
|
||
|
|
//! ```bash
|
||
|
|
//! cargo run --example 01-error-handling -p vapora-shared
|
||
|
|
//! ```
|
||
|
|
//!
|
||
|
|
//! ## Expected Output
|
||
|
|
//! ```text
|
||
|
|
//! === Error Handling Example ===
|
||
|
|
//!
|
||
|
|
//! Scenario 1: Invalid Input
|
||
|
|
//! Error: Project name cannot be empty
|
||
|
|
//!
|
||
|
|
//! Scenario 2: Resource Not Found
|
||
|
|
//! Error: Agent not found: agent-99
|
||
|
|
//!
|
||
|
|
//! Scenario 3: Permission Denied
|
||
|
|
//! Error: Unauthorized: Only project owner can modify
|
||
|
|
//!
|
||
|
|
//! Scenario 4: External Service Failure
|
||
|
|
//! Error: LLM service unavailable: Connection timeout
|
||
|
|
//! Context: Caused by network timeout after 30s
|
||
|
|
//! ```
|
||
|
|
|
||
|
|
use std::fmt;
|
||
|
|
|
||
|
|
// Define custom error types for VAPORA
|
||
|
|
#[derive(Debug)]
|
||
|
|
enum VaporaError {
|
||
|
|
InvalidInput { message: String },
|
||
|
|
NotFound { resource_type: String, id: String },
|
||
|
|
Unauthorized { reason: String },
|
||
|
|
ServiceUnavailable { service: String, cause: String },
|
||
|
|
}
|
||
|
|
|
||
|
|
impl fmt::Display for VaporaError {
|
||
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
|
|
match self {
|
||
|
|
VaporaError::InvalidInput { message } => write!(f, "Invalid input: {}", message),
|
||
|
|
VaporaError::NotFound { resource_type, id } => {
|
||
|
|
write!(f, "{} not found: {}", resource_type, id)
|
||
|
|
}
|
||
|
|
VaporaError::Unauthorized { reason } => write!(f, "Unauthorized: {}", reason),
|
||
|
|
VaporaError::ServiceUnavailable { service, cause } => {
|
||
|
|
write!(f, "{} service unavailable: {}", service, cause)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl std::error::Error for VaporaError {}
|
||
|
|
|
||
|
|
// Type alias for Result<T> with VaporaError
|
||
|
|
type Result<T> = std::result::Result<T, VaporaError>;
|
||
|
|
|
||
|
|
// Example functions that return Result<T>
|
||
|
|
fn validate_project_name(name: &str) -> Result<()> {
|
||
|
|
if name.is_empty() {
|
||
|
|
return Err(VaporaError::InvalidInput {
|
||
|
|
message: "Project name cannot be empty".to_string(),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn get_agent(agent_id: &str) -> Result<String> {
|
||
|
|
if agent_id.starts_with("agent-") && agent_id.len() > 6 {
|
||
|
|
Ok(format!("Agent: {}", agent_id))
|
||
|
|
} else {
|
||
|
|
Err(VaporaError::NotFound {
|
||
|
|
resource_type: "Agent".to_string(),
|
||
|
|
id: agent_id.to_string(),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn authorize_modification(user_is_owner: bool) -> Result<()> {
|
||
|
|
if !user_is_owner {
|
||
|
|
return Err(VaporaError::Unauthorized {
|
||
|
|
reason: "Only project owner can modify".to_string(),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn call_external_llm_service() -> Result<String> {
|
||
|
|
// Simulate a service that fails
|
||
|
|
Err(VaporaError::ServiceUnavailable {
|
||
|
|
service: "LLM".to_string(),
|
||
|
|
cause: "Connection timeout".to_string(),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Function that chains error handling
|
||
|
|
fn create_and_configure_project(name: &str) -> Result<String> {
|
||
|
|
// Step 1: Validate input
|
||
|
|
validate_project_name(name)?;
|
||
|
|
|
||
|
|
// Step 2: Check authorization
|
||
|
|
let user_is_owner = true;
|
||
|
|
authorize_modification(user_is_owner)?;
|
||
|
|
|
||
|
|
// Step 3: Return success
|
||
|
|
Ok(format!("Project '{}' created successfully", name))
|
||
|
|
}
|
||
|
|
|
||
|
|
fn main() {
|
||
|
|
println!("=== Error Handling Example ===\n");
|
||
|
|
|
||
|
|
// Scenario 1: Invalid Input
|
||
|
|
println!("Scenario 1: Invalid Input");
|
||
|
|
match validate_project_name("") {
|
||
|
|
Ok(_) => println!("✓ Project name is valid"),
|
||
|
|
Err(e) => println!("✗ Error: {}", e),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario 2: Resource Not Found
|
||
|
|
println!("\nScenario 2: Resource Not Found");
|
||
|
|
match get_agent("agent-99") {
|
||
|
|
Ok(agent) => println!("✓ {}", agent),
|
||
|
|
Err(e) => println!("✗ Error: {}", e),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario 3: Permission Denied
|
||
|
|
println!("\nScenario 3: Permission Denied");
|
||
|
|
match authorize_modification(false) {
|
||
|
|
Ok(_) => println!("✓ Authorization granted"),
|
||
|
|
Err(e) => println!("✗ Error: {}", e),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario 4: External Service Failure
|
||
|
|
println!("\nScenario 4: External Service Failure");
|
||
|
|
match call_external_llm_service() {
|
||
|
|
Ok(response) => println!("✓ Service response: {}", response),
|
||
|
|
Err(e) => {
|
||
|
|
println!("✗ Error: {}", e);
|
||
|
|
println!(" → Retrying with fallback provider...");
|
||
|
|
println!(" → Using Ollama (local) instead");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario 5: Error Propagation with ?
|
||
|
|
println!("\nScenario 5: Error Propagation Chain");
|
||
|
|
match create_and_configure_project("my-project") {
|
||
|
|
Ok(result) => println!("✓ {}", result),
|
||
|
|
Err(e) => println!("✗ Error: {}", e),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario 6: Error Chain with invalid input
|
||
|
|
println!("\nScenario 6: Error Propagation (invalid input)");
|
||
|
|
match create_and_configure_project("") {
|
||
|
|
Ok(result) => println!("✓ {}", result),
|
||
|
|
Err(e) => println!("✗ Error: {}", e),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 7: Best practices summary
|
||
|
|
println!("\n=== Error Handling Best Practices ===");
|
||
|
|
println!("1. Use Result<T> for fallible operations");
|
||
|
|
println!("2. Use ? operator for error propagation");
|
||
|
|
println!("3. Add context to errors (what went wrong, why)");
|
||
|
|
println!("4. Implement Debug and Display for error types");
|
||
|
|
println!("5. Handle errors at the boundary (API layer)");
|
||
|
|
println!("6. Provide meaningful error messages to users");
|
||
|
|
println!("7. Log errors with appropriate levels (ERROR, WARN)");
|
||
|
|
println!("8. Consider retry strategies for transient errors");
|
||
|
|
}
|