Skip to main content

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 {}