use_diagnostic_label/
lib.rs1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5
6use use_diagnostic_message::DiagnosticMessage;
7use use_diagnostic_span::DiagnosticSpan;
8
9#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
11pub enum DiagnosticLabelKind {
12 Primary,
14 Secondary,
16 Help,
18 Note,
20}
21
22impl DiagnosticLabelKind {
23 #[must_use]
25 pub const fn as_str(self) -> &'static str {
26 match self {
27 Self::Primary => "primary",
28 Self::Secondary => "secondary",
29 Self::Help => "help",
30 Self::Note => "note",
31 }
32 }
33}
34
35impl fmt::Display for DiagnosticLabelKind {
36 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
37 formatter.write_str(self.as_str())
38 }
39}
40
41#[derive(Clone, Debug, Eq, Hash, PartialEq)]
43pub struct DiagnosticLabel {
44 kind: DiagnosticLabelKind,
45 message: DiagnosticMessage,
46 span: Option<DiagnosticSpan>,
47}
48
49impl DiagnosticLabel {
50 #[must_use]
52 pub const fn new(
53 kind: DiagnosticLabelKind,
54 message: DiagnosticMessage,
55 span: Option<DiagnosticSpan>,
56 ) -> Self {
57 Self {
58 kind,
59 message,
60 span,
61 }
62 }
63
64 #[must_use]
66 pub const fn primary(message: DiagnosticMessage, span: DiagnosticSpan) -> Self {
67 Self::new(DiagnosticLabelKind::Primary, message, Some(span))
68 }
69
70 #[must_use]
72 pub const fn secondary(message: DiagnosticMessage, span: DiagnosticSpan) -> Self {
73 Self::new(DiagnosticLabelKind::Secondary, message, Some(span))
74 }
75
76 #[must_use]
78 pub const fn help(message: DiagnosticMessage) -> Self {
79 Self::new(DiagnosticLabelKind::Help, message, None)
80 }
81
82 #[must_use]
84 pub const fn note(message: DiagnosticMessage) -> Self {
85 Self::new(DiagnosticLabelKind::Note, message, None)
86 }
87
88 #[must_use]
90 pub const fn kind(&self) -> DiagnosticLabelKind {
91 self.kind
92 }
93
94 #[must_use]
96 pub const fn message(&self) -> &DiagnosticMessage {
97 &self.message
98 }
99
100 #[must_use]
102 pub const fn span(&self) -> Option<&DiagnosticSpan> {
103 self.span.as_ref()
104 }
105}
106
107impl fmt::Display for DiagnosticLabel {
108 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
109 self.message.fmt(formatter)
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::{DiagnosticLabel, DiagnosticLabelKind};
116 use use_diagnostic_message::DiagnosticMessage;
117 use use_diagnostic_span::{DiagnosticPosition, DiagnosticSpan};
118
119 fn test_span() -> DiagnosticSpan {
120 let start = DiagnosticPosition::new(2, 5).expect("position should be valid");
121 let end = DiagnosticPosition::new(2, 9).expect("position should be valid");
122 DiagnosticSpan::without_source(start, end).expect("span should be valid")
123 }
124
125 #[test]
126 fn creates_primary_label_with_span() {
127 let label = DiagnosticLabel::primary(
128 DiagnosticMessage::new("invalid value").expect("message should be valid"),
129 test_span(),
130 );
131
132 assert_eq!(label.kind(), DiagnosticLabelKind::Primary);
133 assert!(label.span().is_some());
134 }
135
136 #[test]
137 fn creates_secondary_label_with_span() {
138 let label = DiagnosticLabel::secondary(
139 DiagnosticMessage::new("defined here").expect("message should be valid"),
140 test_span(),
141 );
142
143 assert_eq!(label.kind(), DiagnosticLabelKind::Secondary);
144 assert!(label.span().is_some());
145 }
146
147 #[test]
148 fn creates_help_label_without_span() {
149 let label = DiagnosticLabel::help(
150 DiagnosticMessage::new("provide the missing field").expect("message should be valid"),
151 );
152
153 assert_eq!(label.kind(), DiagnosticLabelKind::Help);
154 assert!(label.span().is_none());
155 }
156
157 #[test]
158 fn display_and_debug_do_not_assume_renderer_output() {
159 let label = DiagnosticLabel::note(
160 DiagnosticMessage::new("plain context").expect("message should be valid"),
161 );
162
163 assert_eq!(label.to_string(), "plain context");
164 assert!(format!("{label:?}").contains("Note"));
165 }
166}