1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone, PartialEq)]
10pub struct SourceMapping {
11 pub generated_offset: u32,
12 pub source_file: String,
13 pub source_line: u32,
14 pub source_col: u32,
15 pub name: Option<String>,
16}
17
18#[allow(dead_code)]
20pub struct SourceMap {
21 mappings: Vec<SourceMapping>,
22 source_files: Vec<String>,
23}
24
25#[allow(dead_code)]
26impl SourceMap {
27 pub fn new() -> Self {
28 Self {
29 mappings: Vec::new(),
30 source_files: Vec::new(),
31 }
32 }
33
34 pub fn add_mapping(
36 &mut self,
37 generated_offset: u32,
38 source_file: &str,
39 source_line: u32,
40 source_col: u32,
41 name: Option<&str>,
42 ) {
43 if !self.source_files.contains(&source_file.to_string()) {
44 self.source_files.push(source_file.to_string());
45 }
46 self.mappings.push(SourceMapping {
47 generated_offset,
48 source_file: source_file.to_string(),
49 source_line,
50 source_col,
51 name: name.map(|s| s.to_string()),
52 });
53 self.mappings.sort_unstable_by_key(|m| m.generated_offset);
54 }
55
56 pub fn lookup(&self, generated_offset: u32) -> Option<&SourceMapping> {
58 if self.mappings.is_empty() {
59 return None;
60 }
61 match self
62 .mappings
63 .binary_search_by_key(&generated_offset, |m| m.generated_offset)
64 {
65 Ok(i) => Some(&self.mappings[i]),
66 Err(i) => {
67 if i == 0 {
68 None
69 } else {
70 Some(&self.mappings[i - 1])
71 }
72 }
73 }
74 }
75
76 pub fn mapping_count(&self) -> usize {
77 self.mappings.len()
78 }
79
80 pub fn source_file_count(&self) -> usize {
81 self.source_files.len()
82 }
83
84 pub fn source_files(&self) -> &[String] {
85 &self.source_files
86 }
87
88 pub fn is_empty(&self) -> bool {
89 self.mappings.is_empty()
90 }
91
92 pub fn mappings_for_file(&self, file: &str) -> Vec<&SourceMapping> {
94 self.mappings
95 .iter()
96 .filter(|m| m.source_file == file)
97 .collect()
98 }
99
100 pub fn clear(&mut self) {
101 self.mappings.clear();
102 self.source_files.clear();
103 }
104}
105
106impl Default for SourceMap {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112pub fn new_source_map() -> SourceMap {
113 SourceMap::new()
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn add_and_lookup_exact() {
122 let mut sm = new_source_map();
123 sm.add_mapping(100, "main.rs", 10, 5, None);
124 let m = sm.lookup(100).expect("should succeed");
125 assert_eq!(m.source_line, 10);
126 }
127
128 #[test]
129 fn lookup_between_entries() {
130 let mut sm = new_source_map();
131 sm.add_mapping(0, "a.rs", 1, 0, None);
132 sm.add_mapping(50, "a.rs", 5, 0, None);
133 let m = sm.lookup(25).expect("should succeed");
134 assert_eq!(m.source_line, 1);
135 }
136
137 #[test]
138 fn lookup_before_first_returns_none() {
139 let mut sm = new_source_map();
140 sm.add_mapping(10, "a.rs", 1, 0, None);
141 assert!(sm.lookup(0).is_none());
142 }
143
144 #[test]
145 fn source_file_deduplication() {
146 let mut sm = new_source_map();
147 sm.add_mapping(0, "a.rs", 1, 0, None);
148 sm.add_mapping(1, "a.rs", 2, 0, None);
149 assert_eq!(sm.source_file_count(), 1);
150 }
151
152 #[test]
153 fn multiple_source_files() {
154 let mut sm = new_source_map();
155 sm.add_mapping(0, "a.rs", 1, 0, None);
156 sm.add_mapping(10, "b.rs", 1, 0, None);
157 assert_eq!(sm.source_file_count(), 2);
158 }
159
160 #[test]
161 fn mappings_for_file() {
162 let mut sm = new_source_map();
163 sm.add_mapping(0, "a.rs", 1, 0, None);
164 sm.add_mapping(10, "b.rs", 1, 0, None);
165 assert_eq!(sm.mappings_for_file("a.rs").len(), 1);
166 }
167
168 #[test]
169 fn named_mapping() {
170 let mut sm = new_source_map();
171 sm.add_mapping(0, "a.rs", 1, 0, Some("main"));
172 let m = sm.lookup(0).expect("should succeed");
173 assert_eq!(m.name.as_deref(), Some("main"));
174 }
175
176 #[test]
177 fn empty_map_is_empty() {
178 let sm = new_source_map();
179 assert!(sm.is_empty());
180 }
181
182 #[test]
183 fn clear() {
184 let mut sm = new_source_map();
185 sm.add_mapping(0, "a.rs", 1, 0, None);
186 sm.clear();
187 assert!(sm.is_empty());
188 }
189
190 #[test]
191 fn mapping_count() {
192 let mut sm = new_source_map();
193 sm.add_mapping(0, "a.rs", 1, 0, None);
194 sm.add_mapping(5, "a.rs", 2, 0, None);
195 assert_eq!(sm.mapping_count(), 2);
196 }
197}