Skip to main content

ocelot_base/
source_diagnostic.rs

1use crate::diagnostic_level::DiagnosticLevel;
2use crate::file_path::FilePath;
3use crate::shared_string::SharedString;
4use crate::source_excerpt::SourceExcerpt;
5
6/// Structured source diagnostic with excerpts and inline annotations.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct SourceDiagnostic {
9    /// Severity level of the diagnostic.
10    pub level: DiagnosticLevel,
11    /// Primary file associated with the diagnostic.
12    pub file_path: FilePath,
13    /// User-facing summary message.
14    pub message: SharedString,
15    /// Relevant excerpts that help explain the diagnostic.
16    pub excerpts: Vec<SourceExcerpt>,
17}
18
19impl SourceDiagnostic {
20    /// Creates a source diagnostic from its level, file path, and message.
21    pub fn new(
22        level: DiagnosticLevel,
23        file_path: impl Into<FilePath>,
24        message: impl Into<SharedString>,
25    ) -> Self {
26        Self {
27            level,
28            file_path: file_path.into(),
29            message: message.into(),
30            excerpts: Vec::new(),
31        }
32    }
33
34    /// Returns a copy of this diagnostic with one appended excerpt.
35    pub fn with_excerpt(mut self, excerpt: SourceExcerpt) -> Self {
36        self.excerpts.push(excerpt);
37        self
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::SourceDiagnostic;
44    use crate::diagnostic_level::DiagnosticLevel;
45    use crate::source_annotation::SourceAnnotation;
46    use crate::source_excerpt::SourceExcerpt;
47    use crate::span::Span;
48
49    #[test]
50    fn source_diagnostic_tracks_level_file_and_excerpts() {
51        let diagnostic = SourceDiagnostic::new(
52            DiagnosticLevel::Error,
53            "examples/test.ocelot",
54            "unresolved identifier `value`",
55        )
56        .with_excerpt(
57            SourceExcerpt::new("examples/test.ocelot", 2, "println(value);")
58                .with_annotation(SourceAnnotation::new(Span::new(8, 13), "not found")),
59        );
60
61        assert_eq!(diagnostic.level, DiagnosticLevel::Error);
62        assert_eq!(diagnostic.file_path.as_str(), "examples/test.ocelot");
63        assert_eq!(diagnostic.message, "unresolved identifier `value`");
64        assert_eq!(diagnostic.excerpts.len(), 1);
65        assert_eq!(diagnostic.excerpts[0].line_number, 2);
66        assert_eq!(diagnostic.excerpts[0].annotations.len(), 1);
67    }
68}