devalang_wasm/tools/logger/
structured_error.rs1#[derive(Debug, Clone)]
3pub struct StructuredError {
4 pub message: String,
6 pub file_path: Option<String>,
8 pub line: Option<usize>,
10 pub column: Option<usize>,
12 pub error_type: Option<String>,
14 pub suggestion: Option<String>,
16 pub stacktrace: Vec<String>,
18}
19
20impl StructuredError {
21 pub fn new(message: impl Into<String>) -> Self {
23 Self {
24 message: message.into(),
25 file_path: None,
26 line: None,
27 column: None,
28 error_type: None,
29 suggestion: None,
30 stacktrace: Vec::new(),
31 }
32 }
33
34 pub fn with_file(mut self, path: impl Into<String>) -> Self {
36 self.file_path = Some(path.into());
37 self
38 }
39
40 pub fn with_location(mut self, line: usize, column: usize) -> Self {
42 self.line = Some(line);
43 self.column = Some(column);
44 self
45 }
46
47 pub fn with_type(mut self, error_type: impl Into<String>) -> Self {
49 self.error_type = Some(error_type.into());
50 self
51 }
52
53 pub fn with_suggestion(mut self, suggestion: impl Into<String>) -> Self {
55 self.suggestion = Some(suggestion.into());
56 self
57 }
58
59 pub fn add_stacktrace(mut self, entry: impl Into<String>) -> Self {
61 self.stacktrace.push(entry.into());
62 self
63 }
64
65 pub fn build_details(&self) -> Vec<String> {
67 let mut details = Vec::new();
68
69 if let (Some(file), Some(line), Some(col)) = (&self.file_path, self.line, self.column) {
71 details.push(format!("path: {file}:{line}:{col}"));
72 } else if let (Some(file), Some(line)) = (&self.file_path, self.line) {
73 details.push(format!("path: {file}:{line}"));
74 } else if let Some(file) = &self.file_path {
75 details.push(format!("path: {file}"));
76 }
77
78 if let Some(ref error_type) = self.error_type {
80 details.push(format!("code: {error_type}"));
81 }
82
83 for entry in &self.stacktrace {
85 details.push(entry.clone());
86 }
87
88 if let Some(ref suggestion) = self.suggestion {
90 details.push(format!("help: {suggestion}"));
91 }
92
93 details
94 }
95
96 pub fn build_colored_details(&self) -> Vec<(String, String)> {
99 let mut details = Vec::new();
100
101 if let (Some(file), Some(line), Some(col)) = (&self.file_path, self.line, self.column) {
103 details.push(("path".to_string(), format!("{file}:{line}:{col}")));
104 } else if let (Some(file), Some(line)) = (&self.file_path, self.line) {
105 details.push(("path".to_string(), format!("{file}:{line}")));
106 } else if let Some(file) = &self.file_path {
107 details.push(("path".to_string(), file.clone()));
108 }
109
110 if let Some(ref error_type) = self.error_type {
112 details.push(("code".to_string(), error_type.clone()));
113 }
114
115 for entry in &self.stacktrace {
117 details.push(("trace".to_string(), entry.clone()));
118 }
119
120 if let Some(ref suggestion) = self.suggestion {
122 details.push(("help".to_string(), suggestion.clone()));
123 }
124
125 details
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_structured_error_builder() {
135 let error = StructuredError::new("Unknown statement")
136 .with_file("test.deva")
137 .with_location(5, 3)
138 .with_type("SyntaxError")
139 .with_suggestion("Did you mean 'sleep' ?");
140
141 assert_eq!(error.message, "Unknown statement");
142 assert_eq!(error.file_path, Some("test.deva".to_string()));
143 assert_eq!(error.line, Some(5));
144 assert_eq!(error.column, Some(3));
145 assert_eq!(error.error_type, Some("SyntaxError".to_string()));
146 assert_eq!(error.suggestion, Some("Did you mean 'sleep' ?".to_string()));
147 }
148
149 #[test]
150 fn test_build_details() {
151 let error = StructuredError::new("Test error")
152 .with_file("test.deva")
153 .with_location(10, 5)
154 .with_type("RuntimeError")
155 .add_stacktrace("at collector.rs:123")
156 .with_suggestion("Did you mean 'print' ?");
157
158 let details = error.build_details();
159 assert!(details.iter().any(|d| d.contains("at:")));
160 assert!(details.iter().any(|d| d.contains("code:")));
161 assert!(details.iter().any(|d| d.contains("help:")));
162 }
163
164 #[test]
165 fn test_build_colored_details() {
166 let error = StructuredError::new("Test error")
167 .with_file("test.deva")
168 .with_location(10, 5)
169 .with_type("RuntimeError")
170 .with_suggestion("Did you mean 'print' ?");
171
172 let details = error.build_colored_details();
173 assert_eq!(details.len(), 3); assert_eq!(details[0].0, "at");
175 assert_eq!(details[1].0, "code");
176 assert_eq!(details[2].0, "help");
177 }
178}