use rootcause::prelude::*;
fn validate_email(email: &str) -> Result<(), Report> {
if !email.contains('@') {
return Err(report!("Invalid email format"));
}
if email.len() < 3 {
return Err(report!("Email too short: {}", email));
}
Ok(())
}
fn validate_user_input(email: &str, age: i32) -> Result<(), Report> {
validate_email(email).context("Email validation failed")?;
if !(0..=150).contains(&age) {
return Err(report!("Age out of valid range: {}", age));
}
Ok(())
}
fn validate_password(password: &str) -> Result<(), Report> {
if password.len() < 8 {
bail!("Password too short: minimum 8 characters");
}
Ok(())
}
#[derive(Debug)]
enum OrderError {
InvalidQuantity { min: i32, max: i32, actual: i32 },
InvalidDiscount { reason: String },
}
impl std::fmt::Display for OrderError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
OrderError::InvalidQuantity { min, max, actual } => {
write!(f, "Quantity {actual} outside valid range [{min}, {max}]")
}
OrderError::InvalidDiscount { reason } => {
write!(f, "Invalid discount: {reason}")
}
}
}
}
impl std::error::Error for OrderError {}
fn validate_order(email: &str, quantity: i32, discount_percent: f32) -> Result<(), Report> {
validate_email(email).context("Customer email validation failed")?;
if !(1..=100).contains(&quantity) {
bail!(OrderError::InvalidQuantity {
min: 1,
max: 100,
actual: quantity,
});
}
if !(0.0..=50.0).contains(&discount_percent) {
bail!(OrderError::InvalidDiscount {
reason: format!("{discount_percent}% exceeds maximum allowed discount of 50%"),
});
}
Ok(())
}
fn main() {
println!("Creating errors with report!():\n");
if let Err(report) = validate_user_input("invalid-email", 25) {
eprintln!("{report}\n");
}
println!("Using bail!() as shorthand:\n");
if let Err(report) = validate_password("short") {
eprintln!("{report}\n");
}
println!("Composing different error types:\n");
if let Err(report) = validate_order("invalid-email", 150, 60.0) {
eprintln!("{report}\n");
}
}