Skip to main content

shaperail_codegen/diagnostics/
types.rs

1use crate::diagnostics::registry::{lookup, Severity};
2use std::path::PathBuf;
3
4/// A diagnostic emitted by the codegen for a resource file.
5#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
6pub struct Diagnostic {
7    pub code: &'static str,
8    pub error: String,
9    pub fix: String,
10    pub example: String,
11
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub span: Option<Span>,
14
15    pub severity: Severity,
16
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub doc_url: Option<String>,
19}
20
21/// Source position for a diagnostic.
22///
23/// `line` and `col` are 1-indexed. `col` is a 1-indexed UTF-8 byte column
24/// (not a grapheme or character index). `end_line` / `end_col` describe an
25/// **exclusive** end — i.e. a single-character span at line 3 col 5 is
26/// `(line: 3, col: 5, end_line: 3, end_col: 6)`. When the parser cannot
27/// determine an end position, set `end_line == line` and `end_col == col`
28/// (a zero-width span pointing at the start).
29#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
30pub struct Span {
31    pub file: PathBuf,
32    pub line: u32,
33    pub col: u32,
34    pub end_line: u32,
35    pub end_col: u32,
36}
37
38impl Diagnostic {
39    /// Construct a diagnostic. Severity and doc_url come from the registry.
40    /// In debug builds, panics if `code` is not in the registry — keeps the
41    /// registry authoritative. In release builds, falls back to Severity::Error.
42    pub fn error(
43        code: &'static str,
44        error: impl Into<String>,
45        fix: impl Into<String>,
46        example: impl Into<String>,
47    ) -> Self {
48        let severity = lookup(code).map(|e| e.severity).unwrap_or_else(|| {
49            debug_assert!(false, "diagnostic code {code} not in registry");
50            Severity::Error
51        });
52        Self {
53            code,
54            error: error.into(),
55            fix: fix.into(),
56            example: example.into(),
57            span: None,
58            severity,
59            doc_url: Some(format!("https://shaperail.io/errors/{code}.html")),
60        }
61    }
62
63    /// Attach a source position to this diagnostic.
64    pub fn with_span(mut self, span: Span) -> Self {
65        self.span = Some(span);
66        self
67    }
68}