Skip to main content

xsd_schema/document/
source_spans.rs

1//! Per-node source span tracking for the document buffer.
2//!
3//! [`NodeSourceSpans`] maps node references to byte ranges in the original
4//! XML source, enabling error messages that point to the exact location.
5
6use std::collections::HashMap;
7
8use crate::parser::location::SourceSpan;
9
10/// Mapping from node ref → [`SourceSpan`] (byte range in the source document).
11///
12/// Only populated when [`BufferDocumentOptions::track_source_locations`](super::BufferDocumentOptions::track_source_locations)
13/// is enabled.
14#[derive(Debug, Default)]
15pub struct NodeSourceSpans {
16    map: HashMap<u32, SourceSpan>,
17}
18
19impl NodeSourceSpans {
20    /// Creates a new empty span table.
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    /// Records (or overwrites) the source span for `node_ref`.
26    pub fn set(&mut self, node_ref: u32, span: SourceSpan) {
27        self.map.insert(node_ref, span);
28    }
29
30    /// Returns the source span for `node_ref`, if recorded.
31    pub fn get(&self, node_ref: u32) -> Option<SourceSpan> {
32        self.map.get(&node_ref).copied()
33    }
34
35    /// Returns the number of nodes with recorded spans.
36    pub fn len(&self) -> usize {
37        self.map.len()
38    }
39
40    /// Returns `true` if no spans have been recorded.
41    pub fn is_empty(&self) -> bool {
42        self.map.is_empty()
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn new_table_is_empty() {
52        let spans = NodeSourceSpans::new();
53        assert!(spans.is_empty());
54        assert_eq!(spans.len(), 0);
55    }
56
57    #[test]
58    fn set_get_round_trip() {
59        let mut spans = NodeSourceSpans::new();
60        let span = SourceSpan::new(10, 25);
61        spans.set(3, span);
62        assert_eq!(spans.get(3), Some(span));
63    }
64
65    #[test]
66    fn unrecorded_node_returns_none() {
67        let spans = NodeSourceSpans::new();
68        assert_eq!(spans.get(42), None);
69    }
70
71    #[test]
72    fn set_overwrites_previous() {
73        let mut spans = NodeSourceSpans::new();
74        spans.set(1, SourceSpan::new(0, 10));
75        spans.set(1, SourceSpan::new(5, 20));
76        assert_eq!(spans.get(1), Some(SourceSpan::new(5, 20)));
77    }
78
79    #[test]
80    fn multiple_nodes_independent() {
81        let mut spans = NodeSourceSpans::new();
82        spans.set(0, SourceSpan::new(0, 10));
83        spans.set(1, SourceSpan::new(10, 20));
84        spans.set(2, SourceSpan::new(20, 30));
85
86        assert_eq!(spans.get(0), Some(SourceSpan::new(0, 10)));
87        assert_eq!(spans.get(1), Some(SourceSpan::new(10, 20)));
88        assert_eq!(spans.get(2), Some(SourceSpan::new(20, 30)));
89        assert_eq!(spans.len(), 3);
90    }
91}