html_conform/finding.rs
1use std::error::Error;
2use std::fmt;
3
4use serde::{Deserialize, Serialize};
5
6/// A source location in the checked HTML input.
7///
8/// Positions are one-based; `byte_offset` is zero-based. A finding has no
9/// location when it cannot be mapped back to a concrete source range.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub struct SourceLocation {
12 /// One-based line number.
13 pub line: u32,
14 /// One-based column number.
15 pub column: u32,
16 /// Zero-based byte offset.
17 pub byte_offset: usize,
18}
19
20impl fmt::Display for SourceLocation {
21 /// `"line:column"` — the same shape `src/infoset.rs`'s old, pre-Phase-08
22 /// `relax_ng::Element::location()` string used to format by hand,
23 /// kept for message-text compatibility now that this struct itself is
24 /// that `Element` impl's `Location` type
25 /// (`relax_ng::ValidationError<L>`'s `Display` impl needs `L: Display`
26 /// to include the location in a finding's message text).
27 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(formatter, "{}:{}", self.line, self.column)
29 }
30}
31
32/// The severity of a conformance finding.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34pub enum Severity {
35 /// The document violates a required conformance rule.
36 Error,
37 /// The document is valid but should be reviewed.
38 Warning,
39 /// Informational diagnostic.
40 Info,
41}
42
43/// A single conformance or parser finding.
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub struct Finding {
46 /// Stable identifier for the rule that emitted this finding.
47 pub rule_id: String,
48 /// Severity assigned by the reporting layer.
49 pub severity: Severity,
50 /// Human-readable explanation of the finding.
51 pub message: String,
52 /// Source location when the reporting layer can establish one.
53 pub location: Option<SourceLocation>,
54}
55
56/// The result of checking one HTML document.
57#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
58pub struct CheckReport {
59 /// Findings in parser order followed by the later validation layers.
60 pub findings: Vec<Finding>,
61}
62
63impl CheckReport {
64 /// Returns whether the report contains an error-level finding.
65 #[must_use]
66 pub fn has_errors(&self) -> bool {
67 self.findings
68 .iter()
69 .any(|finding| finding.severity == Severity::Error)
70 }
71}
72
73/// A technical failure that prevented a document from being checked.
74#[derive(Debug, Clone, PartialEq, Eq)]
75#[non_exhaustive]
76pub enum CheckError {
77 /// A checker component could not be initialized.
78 Initialization {
79 /// Description of the failed component initialization.
80 message: String,
81 },
82}
83
84impl fmt::Display for CheckError {
85 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 Self::Initialization { message } => {
88 write!(formatter, "checker initialization failed: {message}")
89 }
90 }
91 }
92}
93
94impl Error for CheckError {}