Skip to main content

orrery_parser/error/
label.rs

1//! Labeled source spans for diagnostic messages.
2//!
3//! A label associates a message with a span in the source code,
4//! providing context for where an error or warning occurred.
5
6use crate::span::Span;
7
8/// A labeled span in source code.
9///
10/// Labels attach messages to specific locations in the source,
11/// helping users understand where problems occurred and why.
12///
13/// # Primary vs Secondary Labels
14///
15/// - **Primary labels** mark the main location of an error or warning.
16///   There should typically be one primary label per diagnostic.
17/// - **Secondary labels** provide additional context, such as "first defined here"
18///   or "also referenced here".
19///
20/// # Example
21///
22/// ```text
23/// error[E301]: cannot override built-in type `Rectangle`
24///   --> src/main.orr:10:1
25///    |
26/// 10 | type Rectangle = Oval[fill_color="red"];
27///    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type override not supported
28/// ```
29#[derive(Debug, Clone)]
30pub struct Label {
31    span: Span,
32    message: String,
33    is_primary: bool,
34}
35
36impl Label {
37    /// Create a new primary label.
38    ///
39    /// Primary labels mark the main location of an error or warning.
40    pub fn primary(span: Span, message: impl Into<String>) -> Self {
41        Self {
42            span,
43            message: message.into(),
44            is_primary: true,
45        }
46    }
47
48    /// Create a new secondary label.
49    ///
50    /// Secondary labels provide additional context for the diagnostic.
51    pub fn secondary(span: Span, message: impl Into<String>) -> Self {
52        Self {
53            span,
54            message: message.into(),
55            is_primary: false,
56        }
57    }
58
59    /// Get the span this label applies to.
60    pub fn span(&self) -> Span {
61        self.span
62    }
63
64    /// Get the label message.
65    pub fn message(&self) -> &str {
66        &self.message
67    }
68
69    /// Check if this is a primary label.
70    pub fn is_primary(&self) -> bool {
71        self.is_primary
72    }
73
74    /// Check if this is a secondary label.
75    pub fn is_secondary(&self) -> bool {
76        !self.is_primary
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_primary_label() {
86        let span = Span::new(10..20);
87        let label = Label::primary(span, "error here");
88
89        assert_eq!(label.span().start(), 10);
90        assert_eq!(label.span().end(), 20);
91        assert_eq!(label.message(), "error here");
92        assert!(label.is_primary());
93        assert!(!label.is_secondary());
94    }
95
96    #[test]
97    fn test_secondary_label() {
98        let span = Span::new(5..15);
99        let label = Label::secondary(span, "first defined here");
100
101        assert_eq!(label.span().start(), 5);
102        assert_eq!(label.span().end(), 15);
103        assert_eq!(label.message(), "first defined here");
104        assert!(!label.is_primary());
105        assert!(label.is_secondary());
106    }
107}