-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathformatting_hooks.rs
More file actions
206 lines (178 loc) · 6.82 KB
/
formatting_hooks.rs
File metadata and controls
206 lines (178 loc) · 6.82 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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! Formatting hooks for global formatting overrides
//!
//! Formatting hooks vs handlers: Both customize how types are displayed, but:
//! - Handlers (see custom_handler.rs): Applied per-attachment with
//! .attach_custom() or per-context
//! - Hooks (this example): Registered once globally and apply to all instances
//! of a type
//!
//! Use formatting hooks to customize how types are displayed across your entire
//! application:
//! - AttachmentFormatterHook: Control placement (Inline/Appendix/Hidden) and
//! priority
//! - ContextFormatterHook: Customize how error contexts are formatted
use rootcause::{
ReportRef,
handlers::{AttachmentFormattingPlacement, AttachmentFormattingStyle, FormattingFunction},
hooks::{
Hooks, attachment_formatter::AttachmentFormatterHook,
context_formatter::ContextFormatterHook,
},
markers::{Dynamic, Local, Uncloneable},
prelude::*,
report_attachment::ReportAttachmentRef,
};
// Example 1: Attachment placement - control where diagnostic data appears
// Large diagnostic data that would clutter the main error message
struct DatabaseQuery {
sql: String,
params: Vec<String>,
execution_plan: String,
}
impl core::fmt::Display for DatabaseQuery {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "SQL: {}", self.sql)?;
writeln!(f, "Parameters: [{}]", self.params.join(", "))?;
writeln!(f, "\nExecution Plan:")?;
write!(f, "{}", self.execution_plan)
}
}
impl core::fmt::Debug for DatabaseQuery {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "DatabaseQuery {{ sql: {:?}, ... }}", self.sql)
}
}
// Move verbose query diagnostics to appendix instead of cluttering inline
struct DatabaseQueryFormatter;
impl AttachmentFormatterHook<DatabaseQuery> for DatabaseQueryFormatter {
fn preferred_formatting_style(
&self,
_attachment: ReportAttachmentRef<'_, Dynamic>,
formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
match formatting_function {
// for display printing we want the full verbosity level and
// put it in an appendix
FormattingFunction::Display => AttachmentFormattingStyle {
placement: AttachmentFormattingPlacement::Appendix {
appendix_name: "Database Query",
},
function: FormattingFunction::Display,
priority: 0,
},
// debug printing of the attachment is more compact so
// we put it inline but at the end
FormattingFunction::Debug => AttachmentFormattingStyle {
placement: AttachmentFormattingPlacement::Inline,
function: FormattingFunction::Debug,
priority: -10,
},
}
}
}
// Example 2: Attachment priority - control ordering
struct ActionRequired(String);
impl core::fmt::Display for ActionRequired {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "⚠️ ACTION REQUIRED: {}", self.0)
}
}
impl core::fmt::Debug for ActionRequired {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
// High priority ensures important actions appear first
struct ActionRequiredFormatter;
impl AttachmentFormatterHook<ActionRequired> for ActionRequiredFormatter {
fn preferred_formatting_style(
&self,
_attachment: ReportAttachmentRef<'_, Dynamic>,
_report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
AttachmentFormattingStyle {
placement: AttachmentFormattingPlacement::Inline,
function: FormattingFunction::Display,
priority: 100, // High priority - shows before other attachments
}
}
}
// Example 3: Context formatting - customize error descriptions globally
#[derive(Debug)]
struct ValidationError {
fields: Vec<(&'static str, &'static str)>,
}
impl core::fmt::Display for ValidationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{} validation errors", self.fields.len())
}
}
impl std::error::Error for ValidationError {}
// Custom formatting for validation errors across the app
struct ValidationErrorFormatter;
impl ContextFormatterHook<ValidationError> for ValidationErrorFormatter {
fn display(
&self,
report: ReportRef<'_, ValidationError, Uncloneable, Local>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
let err = report.current_context();
writeln!(f, "Validation failed:")?;
for (field, reason) in &err.fields {
writeln!(f, " • {}: {}", field, reason)?;
}
Ok(())
}
}
// Example 1: Control attachment placement in output
// Demonstrates placing verbose diagnostic data in the appendix section instead
// of inline
fn demo_attachment_placement() -> Result<(), Report> {
let query = DatabaseQuery {
sql: "SELECT * FROM users WHERE status = ? AND created_at > ?".to_string(),
params: vec!["active".to_string(), "2024-01-01".to_string()],
execution_plan: "Sequential Scan on users\n Filter: (status = 'active')\n Rows: 1234"
.to_string(),
};
Err(report!("Database query failed")
.attach(query)
.attach("Connection timeout after 30s"))
}
fn demo_attachment_priority() -> Result<(), Report> {
Err(report!("Configuration error")
.attach("Debug info: config file not found")
.attach(ActionRequired(
"Create config.toml in project root".to_string(),
))
.attach("Additional context here"))
}
fn demo_context_formatting() -> Result<(), Report> {
let validation = ValidationError {
fields: vec![("email", "invalid format"), ("age", "must be positive")],
};
Err(report!(validation).into_dynamic())
}
fn main() {
// Install formatting hooks
Hooks::new()
.attachment_formatter::<DatabaseQuery, _>(DatabaseQueryFormatter)
.attachment_formatter::<ActionRequired, _>(ActionRequiredFormatter)
.context_formatter::<ValidationError, _>(ValidationErrorFormatter)
.install()
.expect("failed to install hooks");
println!("Example 1: Attachment placement (Appendix)\n");
match demo_attachment_placement() {
Ok(()) => println!("Success"),
Err(error) => eprintln!("{error}\n"),
}
println!("Example 2: Attachment priority\n");
match demo_attachment_priority() {
Ok(()) => println!("Success"),
Err(error) => eprintln!("{error}\n"),
}
println!("Example 3: Context formatting\n");
match demo_context_formatting() {
Ok(()) => println!("Success"),
Err(error) => eprintln!("{error}"),
}
}