diagnostic_collector/
diagnostic_collector.rs1use quarto_error_reporting::{DiagnosticKind, DiagnosticMessage, DiagnosticMessageBuilder};
11use quarto_source_map::{SourceContext, SourceInfo};
12
13struct SimpleCollector {
15 diagnostics: Vec<DiagnosticMessage>,
16}
17
18impl SimpleCollector {
19 fn new() -> Self {
20 Self {
21 diagnostics: Vec::new(),
22 }
23 }
24
25 fn add(&mut self, diagnostic: DiagnosticMessage) {
26 self.diagnostics.push(diagnostic);
27 }
28
29 fn error(&mut self, message: impl Into<String>) {
30 self.add(DiagnosticMessage::error(message.into()));
31 }
32
33 fn warn(&mut self, message: impl Into<String>) {
34 self.add(DiagnosticMessage::warning(message.into()));
35 }
36
37 fn error_at(&mut self, message: impl Into<String>, location: SourceInfo) {
38 self.add(
39 DiagnosticMessageBuilder::error(message.into())
40 .with_location(location)
41 .build(),
42 );
43 }
44
45 fn has_errors(&self) -> bool {
46 self.diagnostics
47 .iter()
48 .any(|d| d.kind == DiagnosticKind::Error)
49 }
50
51 fn diagnostics(&self) -> &[DiagnosticMessage] {
52 &self.diagnostics
53 }
54
55 fn to_text(&self, ctx: Option<&SourceContext>) -> Vec<String> {
56 self.diagnostics.iter().map(|d| d.to_text(ctx)).collect()
57 }
58}
59
60fn main() {
61 println!("=== Example 1: Accumulating multiple errors ===\n");
62
63 let mut collector = SimpleCollector::new();
64
65 collector.error("Missing required field 'title'");
67 collector.warn("Field 'description' is deprecated");
68 collector.error("Invalid value for 'format': expected string, got number");
69
70 if collector.has_errors() {
71 println!(
72 "Validation failed with {} diagnostics:",
73 collector.diagnostics().len()
74 );
75 for text in collector.to_text(None) {
76 println!("{}", text);
77 }
78 }
79
80 println!("\n=== Example 2: Errors with source locations ===\n");
81
82 let mut ctx = SourceContext::new();
83 let file_id = ctx.add_file(
84 "config.yml".to_string(),
85 Some("title: 123\nformat: html\nauthor: John\n".to_string()),
86 );
87
88 let mut collector2 = SimpleCollector::new();
89
90 let loc1 = SourceInfo::original(file_id, 7, 10);
92 collector2.error_at("Title must be a string", loc1);
93
94 let loc2 = SourceInfo::original(file_id, 33, 37);
96 let warning = DiagnosticMessageBuilder::warning("Author field should include email")
97 .with_location(loc2)
98 .add_hint("Use format: 'Name <email@example.com>'")
99 .build();
100 collector2.add(warning);
101
102 println!("Collected diagnostics:");
103 for text in collector2.to_text(Some(&ctx)) {
104 println!("{}", text);
105 println!();
106 }
107
108 println!("=== Example 3: JSON output for all diagnostics ===\n");
109
110 let json_array: Vec<_> = collector2
111 .diagnostics()
112 .iter()
113 .map(|d| d.to_json())
114 .collect();
115
116 println!("{}", serde_json::to_string_pretty(&json_array).unwrap());
117
118 println!("\n=== Example 4: Continuing vs. failing fast ===\n");
119
120 let mut collector3 = SimpleCollector::new();
121
122 for i in 1..=3 {
124 collector3.error(format!("Error in item {}", i));
125 }
126
127 if collector3.has_errors() {
129 eprintln!(
130 "Processing failed with {} errors",
131 collector3.diagnostics().len()
132 );
133 eprintln!("\nErrors:");
134 for diag in collector3.diagnostics() {
135 eprintln!(" - {}", diag.title);
136 }
137 }
138}