#![allow(dead_code)] // Allow dead code for demo purposes use std::io; use thiserror::Error; // Add `thiserror = "1.0"` and `rand = "0.8"` to Cargo.toml use rand::{self, Rng}; #[derive(Debug, Error)] enum UserServiceError { #[error("User with ID '{0}' not found.")] NotFound(u32), #[error("Database error: {0}")] Database(#[from] io::Error), // Example, uses standard library io::Error for simplicity #[error("Validation error for user data: {0}")] Validation(String), #[error("Internal server error: {0}")] Internal(String), } // A simple user struct for demonstration #[derive(Debug, Clone)] struct User { id: u32, name: String, email: String, is_active: bool, } struct UserService { // In a real application, this would be a database connection pool or similar, // ensuring thread-safe access (e.g., using Arc>>). users: Vec, } impl UserService { fn new() -> Self { UserService { users: vec![ User { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string(), is_active: true }, User { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string(), is_active: false }, ], } } /// Attempts to find a user by ID. Returns an error if not found. /// `Result` ensures callers explicitly handle both success and failure, /// preventing accidental use of non-existent data. fn get_user_by_id(&self, id: u32) -> Result<&User, UserServiceError> { self.users.iter() .find(|u| u.id == id) .ok_or_else(|| UserServiceError::NotFound(id)) } /// Attempts to update a user's email. Enforces validation and simulates database constraints. /// This ensures that the application state (users data) remains valid and consistent. fn update_user_email(&mut self, user_id: u32, new_email: &str) -> Result<(), UserServiceError> { // 1. Input validation - prevents creating an invalid user state. if !new_email.contains('@') || !new_email.contains('.') { return Err(UserServiceError::Validation(format!("Invalid email format: {}", new_email))); } // 2. Find user (safe lookup) - `?` propagates `NotFound` error if user isn't there. let user_index = self.users.iter().position(|u| u.id == user_id) .ok_or_else(|| UserServiceError::NotFound(user_id))?; // Simulate a database operation that might fail. let mut rng = rand::thread_rng(); if rng.gen_bool(0.8) { // 80% chance of successful DB update println!("Simulating successful database update for user {} with new email {}.", user_id, new_email); self.users[user_index].email = new_email.to_string(); Ok(()) } else { // If the database operation fails, the state is not updated. Err(UserServiceError::Database(io::Error::new(io::ErrorKind::TimedOut, "DB connection timed out"))) } } /// Simulates a critical operation that might fail and shows graceful error propagation. /// By using `?` and `Result`, failures in sub-operations are propagated up, /// ensuring the overall operation's success is explicit and consistent. fn perform_critical_operation(&mut self, user_id: u32) -> Result<(), UserServiceError> { println!(" Attempting critical operation for user {}...", user_id); // Attempt to update the user's email as part of a larger operation. // If this fails, the error is immediately returned, preventing further processing // with potentially stale or incorrect data. self.update_user_email(user_id, &format!("updated_{}@example.com", user_id))?; // Simulate another internal service call that might fail. let mut rng = rand::thread_rng(); if rng.gen_bool(0.9) { // 90% chance of success println!("Critical operation for user {} completed successfully.", user_id); Ok(()) } else { Err(UserServiceError::Internal(format!("Failed to notify external service for user {}", user_id))) } } } fn main() { let mut service = UserService::new(); // Test get_user_by_id println!("--- Testing User Lookup ---"); match service.get_user_by_id(1) { Ok(user) => println!("Found user: {:?}", user), Err(e) => println!("Error getting user: {}", e), } match service.get_user_by_id(99) { Ok(user) => println!("Found user: {:?}", user), Err(e) => println!("Error getting user: {}", e), } // Test update_user_email println!(" --- Testing Email Updates ---"); match service.update_user_email(1, "alice.updated@example.com") { Ok(_) => println!("Email updated for user 1."), Err(e) => println!("Error updating email for user 1: {}", e), } match service.update_user_email(2, "invalid-email") { Ok(_) => println!("Email updated for user 2."), Err(e) => println!("Error updating email for user 2: {}", e), } match service.update_user_email(99, "nonexistent@example.com") { Ok(_) => println!("Email updated for user 99."), Err(e) => println!("Error updating email for user 99: {}", e), } println!(" --- Testing Critical Operation (repeatedly to show failures) ---"); // This will likely show various error types due to `rand::random()` and propagation. for _ in 0..5 { match service.perform_critical_operation(1) { Ok(_) => println!("Critical operation for user 1 succeeded."), Err(e) => println!("Critical operation for user 1 failed: {}", e), } } }