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}