ass_core/parser/errors/
parse_result.rs1use alloc::vec::Vec;
8
9use super::{
10 parse_error::ParseError,
11 parse_issue::{IssueSeverity, ParseIssue},
12};
13
14pub type ParseResult<T> = Result<T, ParseError>;
20
21#[derive(Debug, Clone)]
27pub struct ParseResultWithIssues<T> {
28 pub result: ParseResult<T>,
30
31 pub issues: Vec<ParseIssue>,
33}
34
35impl<T> ParseResultWithIssues<T> {
36 pub const fn ok(value: T) -> Self {
46 Self {
47 result: Ok(value),
48 issues: Vec::new(),
49 }
50 }
51
52 #[must_use]
62 pub const fn err(error: ParseError) -> Self {
63 Self {
64 result: Err(error),
65 issues: Vec::new(),
66 }
67 }
68
69 pub const fn with_issues(result: ParseResult<T>, issues: Vec<ParseIssue>) -> Self {
80 Self { result, issues }
81 }
82
83 #[must_use]
93 pub fn add_issue(mut self, issue: ParseIssue) -> Self {
94 self.issues.push(issue);
95 self
96 }
97
98 pub fn critical_issues(&self) -> Vec<&ParseIssue> {
107 self.issues
108 .iter()
109 .filter(|issue| matches!(issue.severity, IssueSeverity::Critical))
110 .collect()
111 }
112
113 pub fn has_blocking_issues(&self) -> bool {
122 self.issues.iter().any(ParseIssue::is_blocking)
123 }
124
125 pub fn count_by_severity(&self, severity: IssueSeverity) -> usize {
135 self.issues
136 .iter()
137 .filter(|issue| issue.severity == severity)
138 .count()
139 }
140
141 pub const fn is_ok(&self) -> bool {
147 self.result.is_ok()
148 }
149
150 pub const fn is_err(&self) -> bool {
156 self.result.is_err()
157 }
158}
159
160impl<T> From<ParseResult<T>> for ParseResultWithIssues<T> {
161 fn from(result: ParseResult<T>) -> Self {
165 Self {
166 result,
167 issues: Vec::new(),
168 }
169 }
170}
171
172impl<T> From<T> for ParseResultWithIssues<T> {
173 fn from(value: T) -> Self {
177 Self::ok(value)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::parser::errors::{IssueCategory, IssueSeverity, ParseError, ParseIssue};
185 #[cfg(not(feature = "std"))]
186 use alloc::{
187 string::{String, ToString},
188 vec,
189 };
190
191 #[test]
192 fn parse_result_with_issues_ok() {
193 let result = ParseResultWithIssues::ok(42);
194 assert!(result.is_ok());
195 assert!(!result.is_err());
196 assert_eq!(result.issues.len(), 0);
197 assert!(!result.has_blocking_issues());
198 }
199
200 #[test]
201 fn parse_result_with_issues_err() {
202 let error = ParseError::ExpectedSectionHeader { line: 5 };
203 let result = ParseResultWithIssues::<i32>::err(error);
204 assert!(result.is_err());
205 assert!(!result.is_ok());
206 assert_eq!(result.issues.len(), 0);
207 }
208
209 #[test]
210 fn parse_result_with_issues_add_issue() {
211 let mut result = ParseResultWithIssues::ok(42);
212 let issue = ParseIssue::warning(IssueCategory::Format, "Minor issue".to_string(), 1);
213 result = result.add_issue(issue);
214
215 assert!(result.is_ok());
216 assert_eq!(result.issues.len(), 1);
217 assert!(!result.has_blocking_issues());
218 assert_eq!(result.count_by_severity(IssueSeverity::Warning), 1);
219 assert_eq!(result.count_by_severity(IssueSeverity::Critical), 0);
220 }
221
222 #[test]
223 fn parse_result_with_issues_blocking() {
224 let mut result = ParseResultWithIssues::ok(42);
225 let critical_issue =
226 ParseIssue::critical(IssueCategory::Structure, "Critical error".to_string(), 1);
227 result = result.add_issue(critical_issue);
228
229 assert!(result.has_blocking_issues());
230 assert_eq!(result.critical_issues().len(), 1);
231 assert_eq!(result.count_by_severity(IssueSeverity::Critical), 1);
232 }
233
234 #[test]
235 fn parse_result_with_issues_multiple_severities() {
236 let mut result = ParseResultWithIssues::ok("test");
237
238 result = result.add_issue(ParseIssue::info(
239 IssueCategory::Performance,
240 "Info message".to_string(),
241 1,
242 ));
243
244 result = result.add_issue(ParseIssue::warning(
245 IssueCategory::Style,
246 "Warning message".to_string(),
247 2,
248 ));
249
250 result = result.add_issue(ParseIssue::error(
251 IssueCategory::Color,
252 "Error message".to_string(),
253 3,
254 ));
255
256 assert_eq!(result.issues.len(), 3);
257 assert_eq!(result.count_by_severity(IssueSeverity::Info), 1);
258 assert_eq!(result.count_by_severity(IssueSeverity::Warning), 1);
259 assert_eq!(result.count_by_severity(IssueSeverity::Error), 1);
260 assert_eq!(result.count_by_severity(IssueSeverity::Critical), 0);
261 assert!(!result.has_blocking_issues());
262 }
263
264 #[test]
265 fn parse_result_with_issues_from_parse_result() {
266 let parse_result: ParseResult<i32> = Ok(100);
267 let result_with_issues: ParseResultWithIssues<i32> =
268 ParseResultWithIssues::from(parse_result);
269
270 assert!(result_with_issues.is_ok());
271 assert_eq!(result_with_issues.issues.len(), 0);
272 }
273
274 #[test]
275 fn parse_result_with_issues_from_value() {
276 let result_with_issues = ParseResultWithIssues::from("hello");
277
278 assert!(result_with_issues.is_ok());
279 assert_eq!(result_with_issues.issues.len(), 0);
280 if let Ok(value) = result_with_issues.result {
281 assert_eq!(value, "hello");
282 }
283 }
284
285 #[test]
286 fn parse_result_with_issues_from_error() {
287 let error = ParseError::InvalidFieldFormat { line: 10 };
288 let parse_result: ParseResult<String> = Err(error);
289 let result_with_issues: ParseResultWithIssues<String> =
290 ParseResultWithIssues::from(parse_result);
291
292 assert!(result_with_issues.is_err());
293 assert_eq!(result_with_issues.issues.len(), 0);
294 }
295
296 #[test]
297 fn parse_result_with_issues_pre_collected() {
298 let issues = vec![
299 ParseIssue::warning(IssueCategory::Timing, "Late timing".to_string(), 5),
300 ParseIssue::error(IssueCategory::Font, "Missing font".to_string(), 10),
301 ];
302
303 let result = ParseResultWithIssues::with_issues(Ok("success"), issues);
304
305 assert!(result.is_ok());
306 assert_eq!(result.issues.len(), 2);
307 assert_eq!(result.count_by_severity(IssueSeverity::Warning), 1);
308 assert_eq!(result.count_by_severity(IssueSeverity::Error), 1);
309 }
310}