scrape_core/parser/
warnings.rs1use crate::error::SourceSpan;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
7pub enum WarningSeverity {
8 Info,
10 Warning,
12 RecoveredError,
14}
15
16#[derive(Debug, Clone)]
18pub struct ParseWarning {
19 pub severity: WarningSeverity,
21 pub message: String,
23 pub span: Option<SourceSpan>,
25 pub spec_reference: Option<String>,
27}
28
29impl ParseWarning {
30 #[must_use]
32 pub fn new(severity: WarningSeverity, message: impl Into<String>) -> Self {
33 Self { severity, message: message.into(), span: None, spec_reference: None }
34 }
35
36 #[must_use]
38 pub fn with_span(mut self, span: SourceSpan) -> Self {
39 self.span = Some(span);
40 self
41 }
42
43 #[must_use]
45 pub fn with_spec_reference(mut self, reference: impl Into<String>) -> Self {
46 self.spec_reference = Some(reference.into());
47 self
48 }
49}
50
51#[derive(Debug)]
53pub struct ParseResultWithWarnings<T> {
54 pub result: T,
56 pub warnings: Vec<ParseWarning>,
58}
59
60impl<T> ParseResultWithWarnings<T> {
61 #[must_use]
63 pub fn new(result: T, warnings: Vec<ParseWarning>) -> Self {
64 Self { result, warnings }
65 }
66
67 #[must_use]
69 pub fn has_warnings(&self) -> bool {
70 !self.warnings.is_empty()
71 }
72
73 #[must_use]
75 pub fn warnings_at_least(&self, min_severity: WarningSeverity) -> Vec<&ParseWarning> {
76 self.warnings.iter().filter(|w| w.severity >= min_severity).collect()
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::error::SourcePosition;
84
85 #[test]
86 fn test_warning_creation() {
87 let warning = ParseWarning::new(WarningSeverity::Warning, "unclosed tag");
88 assert_eq!(warning.severity, WarningSeverity::Warning);
89 assert_eq!(warning.message, "unclosed tag");
90 assert!(warning.span.is_none());
91 assert!(warning.spec_reference.is_none());
92 }
93
94 #[test]
95 fn test_warning_with_span() {
96 let span = SourceSpan::new(SourcePosition::new(1, 1, 0), SourcePosition::new(1, 5, 4));
97 let warning = ParseWarning::new(WarningSeverity::Warning, "test").with_span(span);
98
99 assert!(warning.span.is_some());
100 assert_eq!(warning.span.unwrap(), span);
101 }
102
103 #[test]
104 fn test_warning_with_spec_reference() {
105 let warning = ParseWarning::new(WarningSeverity::Info, "test")
106 .with_spec_reference("https://html.spec.whatwg.org/#parse-state");
107
108 assert!(warning.spec_reference.is_some());
109 assert!(warning.spec_reference.unwrap().contains("html.spec.whatwg.org"));
110 }
111
112 #[test]
113 fn test_severity_ordering() {
114 assert!(WarningSeverity::Info < WarningSeverity::Warning);
115 assert!(WarningSeverity::Warning < WarningSeverity::RecoveredError);
116 }
117
118 #[test]
119 fn test_parse_result_with_warnings() {
120 let warnings = vec![
121 ParseWarning::new(WarningSeverity::Info, "info"),
122 ParseWarning::new(WarningSeverity::Warning, "warning"),
123 ParseWarning::new(WarningSeverity::RecoveredError, "error"),
124 ];
125 let result = ParseResultWithWarnings::new(42, warnings);
126
127 assert!(result.has_warnings());
128 assert_eq!(result.warnings.len(), 3);
129 }
130
131 #[test]
132 fn test_warnings_filtering() {
133 let warnings = vec![
134 ParseWarning::new(WarningSeverity::Info, "info"),
135 ParseWarning::new(WarningSeverity::Warning, "warning"),
136 ParseWarning::new(WarningSeverity::RecoveredError, "error"),
137 ];
138 let result = ParseResultWithWarnings::new((), warnings);
139
140 let filtered = result.warnings_at_least(WarningSeverity::Warning);
141 assert_eq!(filtered.len(), 2);
142 assert_eq!(filtered[0].message, "warning");
143 assert_eq!(filtered[1].message, "error");
144 }
145
146 #[test]
147 fn test_no_warnings() {
148 let result = ParseResultWithWarnings::new(42, Vec::new());
149 assert!(!result.has_warnings());
150 assert_eq!(result.warnings_at_least(WarningSeverity::Info).len(), 0);
151 }
152}