Skip to main content

markdown_syntax/
diagnostic.rs

1//! The unified [`Diagnostic`] type shared by the parser, AST validation, and the
2//! serialize/HTML pre-validation.
3
4use alloc::string::String;
5
6use crate::{ast::NodePath, span::Span};
7
8/// How serious a [`Diagnostic`] is.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10#[non_exhaustive]
11pub enum DiagnosticSeverity {
12    /// A non-fatal issue; tolerant parsing continues.
13    Warning,
14    /// A hard error, such as a malformed directive or an invalid AST.
15    Error,
16}
17
18/// A machine-readable category for a [`Diagnostic`].
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20#[non_exhaustive]
21pub enum DiagnosticCode {
22    /// A directive name was malformed.
23    InvalidDirectiveName,
24    /// A container directive (`:::name`) was never closed.
25    UnclosedDirectiveContainer,
26    /// A directive attribute without a valid name was dropped.
27    InvalidDirectiveAttribute,
28    /// AST validation failure (an invalid or unsupported node shape), the single
29    /// code used by `Document::validate` and by serialize/HTML pre-validation.
30    InvalidDocument,
31}
32
33/// A single diagnostic across every domain — parser, AST validation, and the
34/// serialize/HTML pre-validation that wraps it. `span` is optional because a
35/// hand-built AST node may carry no source location.
36#[derive(Clone, Debug, Eq, PartialEq)]
37pub struct Diagnostic {
38    /// How serious the issue is.
39    pub severity: DiagnosticSeverity,
40    /// The machine-readable category.
41    pub code: DiagnosticCode,
42    /// The source location, if known.
43    pub span: Option<Span>,
44    /// The path of the node a validation diagnostic is about, in the document
45    /// validated; `None` for a parser diagnostic.
46    pub path: Option<NodePath>,
47    /// A human-readable description.
48    pub message: String,
49}
50
51impl Diagnostic {
52    /// A parser diagnostic, which always carries a source span.
53    pub fn new(
54        severity: DiagnosticSeverity,
55        code: DiagnosticCode,
56        span: Span,
57        message: impl Into<String>,
58    ) -> Self {
59        Self {
60            severity,
61            code,
62            span: Some(span),
63            path: None,
64            message: message.into(),
65        }
66    }
67
68    /// An AST-validation diagnostic (`Error` severity, `InvalidDocument` code),
69    /// whose span is optional because a hand-built node may lack one.
70    pub fn invalid(span: Option<Span>, message: impl Into<String>) -> Self {
71        Self {
72            severity: DiagnosticSeverity::Error,
73            code: DiagnosticCode::InvalidDocument,
74            span,
75            path: None,
76            message: message.into(),
77        }
78    }
79}