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}