-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathcustom_errors.rs
More file actions
111 lines (96 loc) · 3.44 KB
/
Copy pathcustom_errors.rs
File metadata and controls
111 lines (96 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Creating custom errors with `report!()` and `bail!()`.
//!
//! **Run this example:** `cargo run --example custom_errors`
//!
//! In `basic.rs`, you learned to wrap external errors with `.context()` and
//! `.attach()`. This example shows how to create your own errors from scratch.
//!
//! Two approaches:
//! 1. **Simple validation**: Use `report!()` for quick error messages
//! 2. **Structured errors**: Define custom types with `report!(YourType)`
//!
//! Bonus: `bail!()` is a convenience macro that's shorthand for `return
//! Err(report!(...).into())`
//!
//! **What's next?**
//! - Want to understand type preservation? → `typed_reports.rs`
//! - Need lazy evaluation for performance? → `lazy_evaluation.rs`
//! - See all examples? → `examples/README.md`
use rootcause::prelude::*;
// Use report!() to create errors from scratch
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(())
}
// report!() composes with .attach() and .context() just like external errors
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(())
}
// bail!() is shorthand for: return Err(report!(...).into())
fn validate_password(password: &str) -> Result<(), Report> {
if password.len() < 8 {
bail!("Password too short: minimum 8 characters");
}
Ok(())
}
// Custom error types for structured, domain-specific errors
#[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 {}
// All error types compose naturally - mix report!(), bail!(), and custom types
fn validate_order(email: &str, quantity: i32, discount_percent: f32) -> Result<(), Report> {
// report!() errors compose with .context()
validate_email(email).context("Customer email validation failed")?;
// Custom type errors
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");
}
}