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}