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, 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    /// A human-readable description.
45    pub message: String,
46}
47
48impl Diagnostic {
49    /// A parser diagnostic, which always carries a source span.
50    pub fn new(
51        severity: DiagnosticSeverity,
52        code: DiagnosticCode,
53        span: Span,
54        message: impl Into<String>,
55    ) -> Self {
56        Self {
57            severity,
58            code,
59            span: Some(span),
60            message: message.into(),
61        }
62    }
63
64    /// An AST-validation diagnostic (`Error` severity, `InvalidDocument` code),
65    /// whose span is optional because a hand-built node may lack one.
66    pub fn invalid(span: Option<Span>, message: impl Into<String>) -> Self {
67        Self {
68            severity: DiagnosticSeverity::Error,
69            code: DiagnosticCode::InvalidDocument,
70            span,
71            message: message.into(),
72        }
73    }
74}