shaperail_codegen/span.rs
1//! Path-indexed spans. Field paths use dot notation (`schema.email.type`)
2//! mirroring how diagnostics name fields in their `error` strings.
3//!
4//! ## Convention
5//!
6//! Every entry is the span of the **value** at the named field path. A
7//! `lookup("schema.email")` returns the position of the `email` field's
8//! value (the inline mapping or scalar), not the position of the `email:`
9//! key token. Diagnostics consumed by users are about value content, not
10//! key tokens, so the lookup contract is value-first.
11//!
12//! Mapping-key spans are not stored. If a future caller needs them, add a
13//! parallel `key_spans` field — do not overload the value paths.
14
15use crate::diagnostics::Span;
16use std::collections::HashMap;
17use std::path::PathBuf;
18
19/// Map from dotted field path to source span. Built by the saphyr parser;
20/// consumed by `diagnose_resource_with_spans` (Task 4) to attach positions
21/// to diagnostics.
22#[derive(Debug, Default, Clone)]
23pub struct SpanMap {
24 file: PathBuf,
25 inner: HashMap<String, (u32, u32, u32, u32)>,
26}
27
28impl SpanMap {
29 /// Create a new `SpanMap` associated with the given source file path.
30 pub fn new(file: PathBuf) -> Self {
31 Self {
32 file,
33 inner: HashMap::new(),
34 }
35 }
36
37 /// Insert a span for the given dotted path.
38 ///
39 /// Lines and columns are 1-indexed; `end_col` is exclusive.
40 pub fn insert(
41 &mut self,
42 path: impl Into<String>,
43 line: u32,
44 col: u32,
45 end_line: u32,
46 end_col: u32,
47 ) {
48 self.inner
49 .insert(path.into(), (line, col, end_line, end_col));
50 }
51
52 /// Look up the span for the given dotted path, returning a [`Span`] if found.
53 pub fn lookup(&self, path: &str) -> Option<Span> {
54 self.inner
55 .get(path)
56 .map(|&(line, col, end_line, end_col)| Span {
57 file: self.file.clone(),
58 line,
59 col,
60 end_line,
61 end_col,
62 })
63 }
64
65 /// Return `true` if no spans have been inserted.
66 pub fn is_empty(&self) -> bool {
67 self.inner.is_empty()
68 }
69
70 /// Return the number of spans in this map.
71 pub fn len(&self) -> usize {
72 self.inner.len()
73 }
74}