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::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 (e.g. promoted by `parse_strict`, 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    /// Malformed MDX syntax.
27    InvalidMdx,
28    /// A strict-mode parse promoted a configured extension diagnostic to an error.
29    StrictParse,
30    /// AST validation failure (an invalid or unsupported node shape), the single
31    /// code used by `Document::validate` and by serialize/HTML pre-validation.
32    InvalidDocument,
33}
34
35/// A single diagnostic across every domain — parser, AST validation, and the
36/// serialize/HTML pre-validation that wraps it. `span` is optional because a
37/// hand-built AST node may carry no source location.
38#[derive(Clone, Debug, Eq, PartialEq)]
39pub struct Diagnostic {
40    /// How serious the issue is.
41    pub severity: DiagnosticSeverity,
42    /// The machine-readable category.
43    pub code: DiagnosticCode,
44    /// The source location, if known.
45    pub span: Option<Span>,
46    /// A human-readable description.
47    pub message: String,
48}
49
50impl Diagnostic {
51    /// A parser diagnostic, which always carries a source span.
52    pub fn new(
53        severity: DiagnosticSeverity,
54        code: DiagnosticCode,
55        span: Span,
56        message: impl Into<String>,
57    ) -> Self {
58        Self {
59            severity,
60            code,
61            span: Some(span),
62            message: message.into(),
63        }
64    }
65
66    /// An AST-validation diagnostic (`Error` severity, `InvalidDocument` code),
67    /// whose span is optional because a hand-built node may lack one.
68    pub fn invalid(span: Option<Span>, message: impl Into<String>) -> Self {
69        Self {
70            severity: DiagnosticSeverity::Error,
71            code: DiagnosticCode::InvalidDocument,
72            span,
73            message: message.into(),
74        }
75    }
76}