1use codespan_reporting::diagnostic;
16use codespan_reporting::files;
17use serde::Serialize;
18use std::fmt;
19use std::ops;
20
21pub type FileId = usize;
24
25pub type SourceDatabase = files::SimpleFiles<String, String>;
28
29#[derive(Debug, Default, Copy, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
30pub struct SourceLocation {
31 pub offset: usize,
33 pub line: usize,
35 pub column: usize,
37}
38
39#[derive(Default, Copy, Clone, PartialEq, Eq, Serialize)]
40pub struct SourceRange {
41 pub file: FileId,
42 pub start: SourceLocation,
43 pub end: SourceLocation,
44}
45
46#[derive(Debug, Serialize, Clone)]
47#[serde(tag = "kind", rename = "comment")]
48pub struct Comment {
49 pub loc: SourceRange,
50 pub text: String,
51}
52
53#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
54#[serde(rename_all = "snake_case")]
55pub enum EndiannessValue {
56 LittleEndian,
57 BigEndian,
58}
59
60#[derive(Debug, Copy, Clone, Serialize)]
61#[serde(tag = "kind", rename = "endianness_declaration")]
62pub struct Endianness {
63 pub loc: SourceRange,
64 pub value: EndiannessValue,
65}
66
67#[derive(Debug, Clone, Serialize)]
68#[serde(tag = "kind", rename = "tag")]
69pub struct TagValue {
70 pub id: String,
71 pub loc: SourceRange,
72 pub value: usize,
73}
74
75#[derive(Debug, Clone, Serialize)]
76#[serde(tag = "kind", rename = "tag")]
77pub struct TagRange {
78 pub id: String,
79 pub loc: SourceRange,
80 pub range: ops::RangeInclusive<usize>,
81 pub tags: Vec<TagValue>,
82}
83
84#[derive(Debug, Clone, Serialize)]
85#[serde(tag = "kind", rename = "tag")]
86pub struct TagOther {
87 pub id: String,
88 pub loc: SourceRange,
89}
90
91#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
92#[serde(untagged)]
93pub enum Tag {
94 Value(TagValue),
95 Range(TagRange),
96 Other(TagOther),
97}
98
99#[derive(Debug, Serialize, Clone)]
100#[serde(tag = "kind", rename = "constraint")]
101pub struct Constraint {
102 pub id: String,
103 pub loc: SourceRange,
104 pub value: Option<usize>,
105 pub tag_id: Option<String>,
106}
107
108#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
109pub struct FieldKey(pub usize);
110
111#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
112#[serde(tag = "kind")]
113pub enum FieldDesc {
114 #[serde(rename = "checksum_field")]
115 Checksum { field_id: String },
116 #[serde(rename = "padding_field")]
117 Padding { size: usize },
118 #[serde(rename = "size_field")]
119 Size { field_id: String, width: usize },
120 #[serde(rename = "count_field")]
121 Count { field_id: String, width: usize },
122 #[serde(rename = "elementsize_field")]
123 ElementSize { field_id: String, width: usize },
124 #[serde(rename = "body_field")]
125 Body,
126 #[serde(rename = "payload_field")]
127 Payload { size_modifier: Option<String> },
128 #[serde(rename = "fixed_field")]
129 FixedScalar { width: usize, value: usize },
130 #[serde(rename = "fixed_field")]
131 FixedEnum { enum_id: String, tag_id: String },
132 #[serde(rename = "reserved_field")]
133 Reserved { width: usize },
134 #[serde(rename = "array_field")]
135 Array {
136 id: String,
137 width: Option<usize>,
138 type_id: Option<String>,
139 size_modifier: Option<String>,
140 size: Option<usize>,
141 },
142 #[serde(rename = "scalar_field")]
143 Scalar { id: String, width: usize },
144 #[serde(rename = "flag_field")]
147 Flag { id: String, optional_field_ids: Vec<(String, usize)> },
148 #[serde(rename = "typedef_field")]
149 Typedef { id: String, type_id: String },
150 #[serde(rename = "group_field")]
151 Group { group_id: String, constraints: Vec<Constraint> },
152}
153
154#[derive(Debug, Serialize, Clone)]
155pub struct Field {
156 pub loc: SourceRange,
157 #[serde(skip_serializing)]
160 pub key: FieldKey,
161 #[serde(flatten)]
162 pub desc: FieldDesc,
163 pub cond: Option<Constraint>,
164}
165
166#[derive(Debug, Serialize, Clone)]
167#[serde(tag = "kind", rename = "test_case")]
168pub struct TestCase {
169 pub loc: SourceRange,
170 pub input: String,
171}
172
173#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
174pub struct DeclKey(pub usize);
175
176#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
177#[serde(tag = "kind")]
178pub enum DeclDesc {
179 #[serde(rename = "checksum_declaration")]
180 Checksum { id: String, function: String, width: usize },
181 #[serde(rename = "custom_field_declaration")]
182 CustomField { id: String, width: Option<usize>, function: String },
183 #[serde(rename = "enum_declaration")]
184 Enum { id: String, tags: Vec<Tag>, width: usize },
185 #[serde(rename = "packet_declaration")]
186 Packet {
187 id: String,
188 constraints: Vec<Constraint>,
189 fields: Vec<Field>,
190 parent_id: Option<String>,
191 },
192 #[serde(rename = "struct_declaration")]
193 Struct {
194 id: String,
195 constraints: Vec<Constraint>,
196 fields: Vec<Field>,
197 parent_id: Option<String>,
198 },
199 #[serde(rename = "group_declaration")]
200 Group { id: String, fields: Vec<Field> },
201 #[serde(rename = "test_declaration")]
202 Test { type_id: String, test_cases: Vec<TestCase> },
203}
204
205#[derive(Debug, Serialize, Clone)]
206pub struct Decl {
207 pub loc: SourceRange,
208 #[serde(skip_serializing)]
211 pub key: DeclKey,
212 #[serde(flatten)]
213 pub desc: DeclDesc,
214}
215
216#[derive(Debug, Serialize, Clone)]
217pub struct File {
218 pub version: String,
219 pub file: FileId,
220 pub comments: Vec<Comment>,
221 pub endianness: Endianness,
222 pub declarations: Vec<Decl>,
223 #[serde(skip_serializing)]
224 pub max_key: usize,
225}
226
227impl SourceLocation {
228 pub fn new(offset: usize, line_starts: &[usize]) -> SourceLocation {
234 let mut loc = SourceLocation { offset, line: 0, column: offset };
235 for (line, start) in line_starts.iter().enumerate() {
236 if *start > offset {
237 break;
238 }
239 loc = SourceLocation { offset, line, column: offset - start };
240 }
241 loc
242 }
243}
244
245impl SourceRange {
246 pub fn primary(&self) -> diagnostic::Label<FileId> {
247 diagnostic::Label::primary(self.file, self.start.offset..self.end.offset)
248 }
249 pub fn secondary(&self) -> diagnostic::Label<FileId> {
250 diagnostic::Label::secondary(self.file, self.start.offset..self.end.offset)
251 }
252}
253
254impl fmt::Display for SourceRange {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 if self.start.line == self.end.line {
257 write!(f, "{}:{}-{}", self.start.line, self.start.column, self.end.column)
258 } else {
259 write!(
260 f,
261 "{}:{}-{}:{}",
262 self.start.line, self.start.column, self.end.line, self.end.column
263 )
264 }
265 }
266}
267
268impl fmt::Debug for SourceRange {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 f.debug_struct("SourceRange").finish_non_exhaustive()
271 }
272}
273
274impl ops::Add<SourceRange> for SourceRange {
275 type Output = SourceRange;
276
277 fn add(self, rhs: SourceRange) -> SourceRange {
278 assert_eq!(self.file, rhs.file);
279 SourceRange {
280 file: self.file,
281 start: self.start.min(rhs.start),
282 end: self.end.max(rhs.end),
283 }
284 }
285}
286
287impl Eq for Endianness {}
288impl PartialEq for Endianness {
289 fn eq(&self, other: &Self) -> bool {
290 self.value == other.value
292 }
293}
294
295impl Eq for TagValue {}
296impl PartialEq for TagValue {
297 fn eq(&self, other: &Self) -> bool {
298 self.id == other.id && self.value == other.value
300 }
301}
302
303impl Eq for TagRange {}
304impl PartialEq for TagRange {
305 fn eq(&self, other: &Self) -> bool {
306 self.id == other.id && self.range == other.range && self.tags == other.tags
308 }
309}
310
311impl Eq for TagOther {}
312impl PartialEq for TagOther {
313 fn eq(&self, other: &Self) -> bool {
314 self.id == other.id
316 }
317}
318
319impl Tag {
320 pub fn id(&self) -> &str {
321 match self {
322 Tag::Value(TagValue { id, .. })
323 | Tag::Range(TagRange { id, .. })
324 | Tag::Other(TagOther { id, .. }) => id,
325 }
326 }
327
328 pub fn loc(&self) -> &SourceRange {
329 match self {
330 Tag::Value(TagValue { loc, .. })
331 | Tag::Range(TagRange { loc, .. })
332 | Tag::Other(TagOther { loc, .. }) => loc,
333 }
334 }
335
336 pub fn value(&self) -> Option<usize> {
337 match self {
338 Tag::Value(TagValue { value, .. }) => Some(*value),
339 Tag::Range(_) | Tag::Other(_) => None,
340 }
341 }
342}
343
344impl Eq for Constraint {}
345impl PartialEq for Constraint {
346 fn eq(&self, other: &Self) -> bool {
347 self.id == other.id && self.value == other.value && self.tag_id == other.tag_id
349 }
350}
351
352impl Eq for TestCase {}
353impl PartialEq for TestCase {
354 fn eq(&self, other: &Self) -> bool {
355 self.input == other.input
357 }
358}
359
360impl Eq for File {}
361impl PartialEq for File {
362 fn eq(&self, other: &Self) -> bool {
363 self.endianness == other.endianness && self.declarations == other.declarations
366 }
367}
368
369impl File {
370 pub fn new(file: FileId) -> File {
371 File {
372 version: "1,0".to_owned(),
373 comments: vec![],
374 endianness: Endianness {
377 loc: SourceRange::default(),
378 value: EndiannessValue::LittleEndian,
379 },
380 declarations: vec![],
381 file,
382 max_key: 0,
383 }
384 }
385
386 pub fn iter_children<'d>(&'d self, decl: &'d Decl) -> impl Iterator<Item = &'d Decl> {
390 self.declarations.iter().filter(|other_decl| other_decl.parent_id() == decl.id())
391 }
392}
393
394impl Eq for Decl {}
395impl PartialEq for Decl {
396 fn eq(&self, other: &Self) -> bool {
397 self.desc == other.desc
399 }
400}
401
402impl Decl {
403 pub fn id(&self) -> Option<&str> {
404 match &self.desc {
405 DeclDesc::Test { .. } => None,
406 DeclDesc::Checksum { id, .. }
407 | DeclDesc::CustomField { id, .. }
408 | DeclDesc::Enum { id, .. }
409 | DeclDesc::Packet { id, .. }
410 | DeclDesc::Struct { id, .. }
411 | DeclDesc::Group { id, .. } => Some(id),
412 }
413 }
414
415 pub fn parent_id(&self) -> Option<&str> {
416 match &self.desc {
417 DeclDesc::Packet { parent_id, .. } | DeclDesc::Struct { parent_id, .. } => {
418 parent_id.as_deref()
419 }
420 _ => None,
421 }
422 }
423
424 pub fn constraints(&self) -> std::slice::Iter<'_, Constraint> {
425 match &self.desc {
426 DeclDesc::Packet { constraints, .. } | DeclDesc::Struct { constraints, .. } => {
427 constraints.iter()
428 }
429 _ => [].iter(),
430 }
431 }
432
433 pub fn fields(&self) -> std::slice::Iter<'_, Field> {
434 match &self.desc {
435 DeclDesc::Packet { fields, .. }
436 | DeclDesc::Struct { fields, .. }
437 | DeclDesc::Group { fields, .. } => fields.iter(),
438 _ => [].iter(),
439 }
440 }
441
442 pub fn payload(&self) -> Option<&Field> {
445 self.fields()
446 .find(|field| matches!(&field.desc, FieldDesc::Payload { .. } | FieldDesc::Body))
447 }
448
449 pub fn payload_size(&self) -> Option<&Field> {
452 self.fields().find(|field| match &field.desc {
453 FieldDesc::Size { field_id, .. } => field_id == "_payload_" || field_id == "_body_",
454 _ => false,
455 })
456 }
457
458 pub fn array_size(&self, id: &str) -> Option<&Field> {
461 self.fields().find(|field| match &field.desc {
462 FieldDesc::Size { field_id, .. } | FieldDesc::Count { field_id, .. } => field_id == id,
463 _ => false,
464 })
465 }
466
467 pub fn kind(&self) -> &str {
468 match &self.desc {
469 DeclDesc::Checksum { .. } => "checksum",
470 DeclDesc::CustomField { .. } => "custom field",
471 DeclDesc::Enum { .. } => "enum",
472 DeclDesc::Packet { .. } => "packet",
473 DeclDesc::Struct { .. } => "struct",
474 DeclDesc::Group { .. } => "group",
475 DeclDesc::Test { .. } => "test",
476 }
477 }
478}
479
480impl Eq for Field {}
481impl PartialEq for Field {
482 fn eq(&self, other: &Self) -> bool {
483 self.desc == other.desc
485 }
486}
487
488impl Field {
489 pub fn id(&self) -> Option<&str> {
490 match &self.desc {
491 FieldDesc::Checksum { .. }
492 | FieldDesc::Padding { .. }
493 | FieldDesc::Size { .. }
494 | FieldDesc::Count { .. }
495 | FieldDesc::ElementSize { .. }
496 | FieldDesc::Body
497 | FieldDesc::Payload { .. }
498 | FieldDesc::FixedScalar { .. }
499 | FieldDesc::FixedEnum { .. }
500 | FieldDesc::Reserved { .. }
501 | FieldDesc::Group { .. } => None,
502 FieldDesc::Array { id, .. }
503 | FieldDesc::Scalar { id, .. }
504 | FieldDesc::Flag { id, .. }
505 | FieldDesc::Typedef { id, .. } => Some(id),
506 }
507 }
508
509 pub fn kind(&self) -> &str {
510 match &self.desc {
511 FieldDesc::Checksum { .. } => "payload",
512 FieldDesc::Padding { .. } => "padding",
513 FieldDesc::Size { .. } => "size",
514 FieldDesc::Count { .. } => "count",
515 FieldDesc::ElementSize { .. } => "elementsize",
516 FieldDesc::Body => "body",
517 FieldDesc::Payload { .. } => "payload",
518 FieldDesc::FixedScalar { .. } | FieldDesc::FixedEnum { .. } => "fixed",
519 FieldDesc::Reserved { .. } => "reserved",
520 FieldDesc::Group { .. } => "group",
521 FieldDesc::Array { .. } => "array",
522 FieldDesc::Scalar { .. } => "scalar",
523 FieldDesc::Flag { .. } => "scalar",
524 FieldDesc::Typedef { .. } => "typedef",
525 }
526 }
527}
528
529#[cfg(test)]
530mod tests {
531 use super::*;
532
533 #[test]
534 fn source_location_new() {
535 let line_starts = &[0, 20, 80, 120, 150];
536 assert_eq!(
537 SourceLocation::new(0, line_starts),
538 SourceLocation { offset: 0, line: 0, column: 0 }
539 );
540 assert_eq!(
541 SourceLocation::new(10, line_starts),
542 SourceLocation { offset: 10, line: 0, column: 10 }
543 );
544 assert_eq!(
545 SourceLocation::new(50, line_starts),
546 SourceLocation { offset: 50, line: 1, column: 30 }
547 );
548 assert_eq!(
549 SourceLocation::new(100, line_starts),
550 SourceLocation { offset: 100, line: 2, column: 20 }
551 );
552 assert_eq!(
553 SourceLocation::new(1000, line_starts),
554 SourceLocation { offset: 1000, line: 4, column: 850 }
555 );
556 }
557
558 #[test]
559 fn source_location_new_no_crash_with_empty_line_starts() {
560 let loc = SourceLocation::new(100, &[]);
561 assert_eq!(loc, SourceLocation { offset: 100, line: 0, column: 100 });
562 }
563}