surf_parse/error.rs
1use serde::{Deserialize, Serialize};
2
3use crate::types::Span;
4
5/// Errors that can occur during parsing.
6#[derive(Debug, Clone, thiserror::Error)]
7pub enum ParseError {
8 #[error("Invalid front matter: {message}")]
9 FrontMatter { message: String, span: Span },
10
11 #[error("Unclosed block directive '{name}' opened at line {line}")]
12 UnclosedBlock { name: String, line: usize },
13
14 #[error("Invalid attribute syntax: {message}")]
15 InvalidAttrs { message: String, span: Span },
16}
17
18/// A diagnostic message produced during parsing.
19///
20/// Diagnostics are non-fatal: the parser continues and produces a best-effort
21/// result even when diagnostics are emitted.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Diagnostic {
24 pub severity: Severity,
25 pub message: String,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub span: Option<Span>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub code: Option<String>,
30 /// Deterministic auto-fix for this diagnostic, when one exists.
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub fix: Option<Fix>,
33}
34
35/// A single span-based text edit against the original source.
36///
37/// `span` is BYTE-offset based (`start_offset`/`end_offset`) against the
38/// CRLF-normalised source the diagnostics were produced from. Offsets always
39/// sit on UTF-8 character boundaries. An empty span (`start_offset ==
40/// end_offset`) is an insertion.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct TextEdit {
43 /// Byte range in the original source to replace.
44 pub span: Span,
45 /// Text spliced in place of the spanned bytes.
46 pub replacement: String,
47}
48
49/// Safety tier of a [`Fix`]. Ordered: `Safe < Suggested`, so a requested tier
50/// of `Suggested` also applies every `Safe` fix.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
52#[serde(rename_all = "lowercase")]
53pub enum FixSafety {
54 /// Mechanical and behaviour-preserving; always safe to auto-apply.
55 Safe,
56 /// Probably right but involves a judgement call (e.g. a rename based on a
57 /// did-you-mean match); applied only when explicitly requested.
58 Suggested,
59}
60
61/// A deterministic auto-fix: one or more text edits applied to the ORIGINAL
62/// source bytes (never an AST round-trip), so untouched bytes are preserved
63/// exactly.
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct Fix {
66 /// Edits to apply, all anchored to the same original source.
67 pub edits: Vec<TextEdit>,
68 /// Safety tier controlling when the fix is auto-applied.
69 pub safety: FixSafety,
70 /// Human-readable description, e.g. `"replace '::section' with '## X'"`.
71 pub description: String,
72}
73
74/// Severity level for diagnostics.
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(rename_all = "lowercase")]
77pub enum Severity {
78 Error,
79 Warning,
80 Info,
81}