anni_common/
diagnostic.rs1use serde::Serialize;
4
5#[derive(Serialize)]
6pub struct Diagnostic<T> {
7 pub message: DiagnosticMessage<T>,
8 pub location: DiagnosticLocation,
9 pub severity: DiagnosticSeverity,
10
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub source: Option<DiagnosticSource>,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub code: Option<DiagnosticCode>,
15 #[serde(skip_serializing_if = "Vec::is_empty")]
16 pub suggestions: Vec<DiagnosticSuggestion>,
17}
18
19impl<T> Diagnostic<T> {
20 pub fn error(message: DiagnosticMessage<T>, location: DiagnosticLocation) -> Self {
21 Self {
22 message,
23 location,
24 severity: DiagnosticSeverity::Error,
25
26 source: None,
27 code: None,
28 suggestions: Vec::new(),
29 }
30 }
31
32 pub fn warning(message: DiagnosticMessage<T>, location: DiagnosticLocation) -> Self {
33 Self {
34 message,
35 location,
36 severity: DiagnosticSeverity::Warning,
37
38 source: None,
39 code: None,
40 suggestions: Vec::new(),
41 }
42 }
43}
44
45pub struct DiagnosticMessage<T> {
46 pub target: T,
47 pub message: String,
48}
49
50impl<T> Serialize for DiagnosticMessage<T> {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: serde::Serializer,
54 {
55 serializer.serialize_str(&self.message)
56 }
57}
58
59#[derive(Clone, Serialize)]
61pub enum MetadataDiagnosticTarget {
62 Identifier(String, Option<u8>, Option<u8>),
63 Tag(String),
64}
65
66impl MetadataDiagnosticTarget {
67 pub fn album(album_id: String) -> Self {
68 Self::Identifier(album_id, None, None)
69 }
70
71 pub fn disc(album_id: String, disc_id: u8) -> Self {
72 Self::Identifier(album_id, Some(disc_id), None)
73 }
74
75 pub fn track(album_id: String, disc_id: u8, track_id: u8) -> Self {
76 Self::Identifier(album_id, Some(disc_id), Some(track_id))
77 }
78}
79
80#[derive(Serialize)]
81pub struct DiagnosticLocation {
82 pub path: String,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub range: Option<DiagnosticRange>,
85}
86
87impl DiagnosticLocation {
88 pub fn simple(path: String) -> DiagnosticLocation {
89 DiagnosticLocation { path, range: None }
90 }
91}
92
93impl DiagnosticLocation {
94 pub fn start_line(&self) -> u32 {
95 self.range.as_ref().map(|r| r.start.line).unwrap_or(0)
96 }
97
98 pub fn start_column(&self) -> Option<u32> {
99 self.range.as_ref().and_then(|r| r.start.column)
100 }
101
102 pub fn end_line(&self) -> Option<u32> {
103 self.range
104 .as_ref()
105 .and_then(|r| r.end.as_ref().map(|p| p.line))
106 }
107
108 pub fn end_column(&self) -> Option<u32> {
109 self.range
110 .as_ref()
111 .and_then(|r| r.end.as_ref().and_then(|p| p.column))
112 }
113}
114
115#[derive(Serialize, Default)]
116pub struct DiagnosticRange {
117 pub start: DiagnosticPosition,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub end: Option<DiagnosticPosition>,
120}
121
122#[derive(Serialize, Default)]
123pub struct DiagnosticPosition {
124 pub line: u32,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub column: Option<u32>,
127}
128
129#[derive(Serialize)]
130#[serde(rename_all = "UPPERCASE")]
131pub enum DiagnosticSeverity {
132 Error,
133 Warning,
134 #[serde(rename = "INFO")]
136 Information,
137 Hint,
139}
140
141impl Default for DiagnosticSeverity {
142 fn default() -> Self {
143 DiagnosticSeverity::Information
144 }
145}
146
147#[derive(Serialize, Default)]
148pub struct DiagnosticSource {
149 pub name: String,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub url: Option<String>,
152}
153
154#[derive(Serialize, Default)]
155pub struct DiagnosticCode {
156 pub value: String,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub url: Option<String>,
159}
160
161impl DiagnosticCode {
162 pub fn new(value: String) -> Self {
163 Self {
164 value,
165 ..Default::default()
166 }
167 }
168}
169
170#[derive(Serialize, Default)]
171pub struct DiagnosticSuggestion {
172 pub range: DiagnosticRange,
173 pub text: String,
174}