#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct SourceMapping {
pub generated_offset: u32,
pub source_file: String,
pub source_line: u32,
pub source_col: u32,
pub name: Option<String>,
}
#[allow(dead_code)]
pub struct SourceMap {
mappings: Vec<SourceMapping>,
source_files: Vec<String>,
}
#[allow(dead_code)]
impl SourceMap {
pub fn new() -> Self {
Self {
mappings: Vec::new(),
source_files: Vec::new(),
}
}
pub fn add_mapping(
&mut self,
generated_offset: u32,
source_file: &str,
source_line: u32,
source_col: u32,
name: Option<&str>,
) {
if !self.source_files.contains(&source_file.to_string()) {
self.source_files.push(source_file.to_string());
}
self.mappings.push(SourceMapping {
generated_offset,
source_file: source_file.to_string(),
source_line,
source_col,
name: name.map(|s| s.to_string()),
});
self.mappings.sort_unstable_by_key(|m| m.generated_offset);
}
pub fn lookup(&self, generated_offset: u32) -> Option<&SourceMapping> {
if self.mappings.is_empty() {
return None;
}
match self
.mappings
.binary_search_by_key(&generated_offset, |m| m.generated_offset)
{
Ok(i) => Some(&self.mappings[i]),
Err(i) => {
if i == 0 {
None
} else {
Some(&self.mappings[i - 1])
}
}
}
}
pub fn mapping_count(&self) -> usize {
self.mappings.len()
}
pub fn source_file_count(&self) -> usize {
self.source_files.len()
}
pub fn source_files(&self) -> &[String] {
&self.source_files
}
pub fn is_empty(&self) -> bool {
self.mappings.is_empty()
}
pub fn mappings_for_file(&self, file: &str) -> Vec<&SourceMapping> {
self.mappings
.iter()
.filter(|m| m.source_file == file)
.collect()
}
pub fn clear(&mut self) {
self.mappings.clear();
self.source_files.clear();
}
}
impl Default for SourceMap {
fn default() -> Self {
Self::new()
}
}
pub fn new_source_map() -> SourceMap {
SourceMap::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_and_lookup_exact() {
let mut sm = new_source_map();
sm.add_mapping(100, "main.rs", 10, 5, None);
let m = sm.lookup(100).expect("should succeed");
assert_eq!(m.source_line, 10);
}
#[test]
fn lookup_between_entries() {
let mut sm = new_source_map();
sm.add_mapping(0, "a.rs", 1, 0, None);
sm.add_mapping(50, "a.rs", 5, 0, None);
let m = sm.lookup(25).expect("should succeed");
assert_eq!(m.source_line, 1);
}
#[test]
fn lookup_before_first_returns_none() {
let mut sm = new_source_map();
sm.add_mapping(10, "a.rs", 1, 0, None);
assert!(sm.lookup(0).is_none());
}
#[test]
fn source_file_deduplication() {
let mut sm = new_source_map();
sm.add_mapping(0, "a.rs", 1, 0, None);
sm.add_mapping(1, "a.rs", 2, 0, None);
assert_eq!(sm.source_file_count(), 1);
}
#[test]
fn multiple_source_files() {
let mut sm = new_source_map();
sm.add_mapping(0, "a.rs", 1, 0, None);
sm.add_mapping(10, "b.rs", 1, 0, None);
assert_eq!(sm.source_file_count(), 2);
}
#[test]
fn mappings_for_file() {
let mut sm = new_source_map();
sm.add_mapping(0, "a.rs", 1, 0, None);
sm.add_mapping(10, "b.rs", 1, 0, None);
assert_eq!(sm.mappings_for_file("a.rs").len(), 1);
}
#[test]
fn named_mapping() {
let mut sm = new_source_map();
sm.add_mapping(0, "a.rs", 1, 0, Some("main"));
let m = sm.lookup(0).expect("should succeed");
assert_eq!(m.name.as_deref(), Some("main"));
}
#[test]
fn empty_map_is_empty() {
let sm = new_source_map();
assert!(sm.is_empty());
}
#[test]
fn clear() {
let mut sm = new_source_map();
sm.add_mapping(0, "a.rs", 1, 0, None);
sm.clear();
assert!(sm.is_empty());
}
#[test]
fn mapping_count() {
let mut sm = new_source_map();
sm.add_mapping(0, "a.rs", 1, 0, None);
sm.add_mapping(5, "a.rs", 2, 0, None);
assert_eq!(sm.mapping_count(), 2);
}
}