use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
pub struct MappingSegment {
pub original_start: u32,
pub original_end: u32,
pub expanded_start: u32,
pub expanded_end: u32,
}
impl MappingSegment {
pub fn new(
original_start: u32,
original_end: u32,
expanded_start: u32,
expanded_end: u32,
) -> Self {
Self {
original_start,
original_end,
expanded_start,
expanded_end,
}
}
pub fn original_len(&self) -> u32 {
self.original_end.saturating_sub(self.original_start)
}
pub fn expanded_len(&self) -> u32 {
self.expanded_end.saturating_sub(self.expanded_start)
}
pub fn contains_original(&self, pos: u32) -> bool {
pos >= self.original_start && pos < self.original_end
}
pub fn contains_expanded(&self, pos: u32) -> bool {
pos >= self.expanded_start && pos < self.expanded_end
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct GeneratedRegion {
pub start: u32,
pub end: u32,
pub source_macro: String,
}
impl GeneratedRegion {
pub fn new(start: u32, end: u32, source_macro: impl Into<String>) -> Self {
Self {
start,
end,
source_macro: source_macro.into(),
}
}
pub fn len(&self) -> u32 {
self.end.saturating_sub(self.start)
}
pub fn is_empty(&self) -> bool {
self.start >= self.end
}
pub fn contains(&self, expanded_pos: u32) -> bool {
expanded_pos >= self.start && expanded_pos < self.end
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct SourceMapping {
pub segments: Vec<MappingSegment>,
pub generated_regions: Vec<GeneratedRegion>,
}
impl SourceMapping {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(segments: usize, generated: usize) -> Self {
Self {
segments: Vec::with_capacity(segments),
generated_regions: Vec::with_capacity(generated),
}
}
pub fn add_segment(&mut self, segment: MappingSegment) {
self.segments.push(segment);
}
pub fn add_generated(&mut self, region: GeneratedRegion) {
self.generated_regions.push(region);
}
pub fn is_empty(&self) -> bool {
self.segments.is_empty() && self.generated_regions.is_empty()
}
pub fn original_to_expanded(&self, pos: u32) -> u32 {
for seg in &self.segments {
if seg.contains_original(pos) {
let offset = pos - seg.original_start;
return seg.expanded_start + offset;
}
}
if let Some(last) = self.segments.last()
&& pos >= last.original_end
{
let delta = pos - last.original_end;
return last.expanded_end + delta;
}
pos
}
pub fn expanded_to_original(&self, pos: u32) -> Option<u32> {
if self.is_in_generated(pos) {
return None;
}
for seg in &self.segments {
if seg.contains_expanded(pos) {
let offset = pos - seg.expanded_start;
return Some(seg.original_start + offset);
}
}
if let Some(last) = self.segments.last()
&& pos >= last.expanded_end
{
let delta = pos - last.expanded_end;
return Some(last.original_end + delta);
}
None
}
pub fn is_in_generated(&self, expanded_pos: u32) -> bool {
self.generated_regions
.iter()
.any(|r| r.contains(expanded_pos))
}
pub fn generated_by(&self, expanded_pos: u32) -> Option<&str> {
self.generated_regions
.iter()
.find(|r| r.contains(expanded_pos))
.map(|r| r.source_macro.as_str())
}
pub fn map_span_to_original(&self, start: u32, length: u32) -> Option<(u32, u32)> {
let end = start + length;
let original_start = self.expanded_to_original(start)?;
let original_end = self.expanded_to_original(end)?;
Some((original_start, original_end.saturating_sub(original_start)))
}
pub fn map_span_to_expanded(&self, start: u32, length: u32) -> (u32, u32) {
let end = start + length;
let expanded_start = self.original_to_expanded(start);
let expanded_end = self.original_to_expanded(end);
(expanded_start, expanded_end.saturating_sub(expanded_start))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_identity_mapping() {
let mapping = SourceMapping::new();
assert_eq!(mapping.original_to_expanded(0), 0);
assert_eq!(mapping.original_to_expanded(100), 100);
assert_eq!(mapping.expanded_to_original(50), None); }
#[test]
fn test_simple_insert() {
let mut mapping = SourceMapping::new();
mapping.add_segment(MappingSegment::new(0, 12, 0, 12));
mapping.add_generated(GeneratedRegion::new(12, 26, "Debug::toString"));
mapping.add_segment(MappingSegment::new(12, 13, 26, 27));
assert_eq!(mapping.original_to_expanded(0), 0);
assert_eq!(mapping.original_to_expanded(5), 5);
assert_eq!(mapping.original_to_expanded(12), 26);
assert_eq!(mapping.expanded_to_original(0), Some(0));
assert_eq!(mapping.expanded_to_original(5), Some(5));
assert_eq!(mapping.expanded_to_original(15), None); assert_eq!(mapping.expanded_to_original(26), Some(12));
assert!(!mapping.is_in_generated(11));
assert!(mapping.is_in_generated(12));
assert!(mapping.is_in_generated(20));
assert!(!mapping.is_in_generated(26));
assert_eq!(mapping.generated_by(15), Some("Debug::toString"));
assert_eq!(mapping.generated_by(5), None);
}
#[test]
fn test_multiple_inserts() {
let mut mapping = SourceMapping::new();
mapping.add_segment(MappingSegment::new(0, 2, 0, 2));
mapping.add_generated(GeneratedRegion::new(2, 4, "macro1"));
mapping.add_segment(MappingSegment::new(2, 4, 4, 6));
mapping.add_generated(GeneratedRegion::new(6, 8, "macro2"));
mapping.add_segment(MappingSegment::new(4, 6, 8, 10));
mapping.add_generated(GeneratedRegion::new(10, 12, "macro3"));
assert_eq!(mapping.original_to_expanded(0), 0);
assert_eq!(mapping.original_to_expanded(2), 4);
assert_eq!(mapping.original_to_expanded(4), 8);
assert_eq!(mapping.expanded_to_original(0), Some(0));
assert_eq!(mapping.expanded_to_original(3), None); assert_eq!(mapping.expanded_to_original(4), Some(2));
assert_eq!(mapping.expanded_to_original(7), None); assert_eq!(mapping.expanded_to_original(8), Some(4));
}
#[test]
fn test_span_mapping() {
let mut mapping = SourceMapping::new();
mapping.add_segment(MappingSegment::new(0, 10, 0, 10));
mapping.add_generated(GeneratedRegion::new(10, 20, "gen"));
mapping.add_segment(MappingSegment::new(10, 20, 20, 30));
assert_eq!(mapping.map_span_to_expanded(2, 5), (2, 5));
assert_eq!(mapping.map_span_to_original(2, 5), Some((2, 5)));
assert_eq!(mapping.map_span_to_expanded(12, 3), (22, 3));
assert_eq!(mapping.map_span_to_original(22, 3), Some((12, 3)));
assert_eq!(mapping.map_span_to_original(12, 5), None);
}
#[test]
fn test_segment_helpers() {
let seg = MappingSegment::new(10, 20, 30, 45);
assert_eq!(seg.original_len(), 10);
assert_eq!(seg.expanded_len(), 15);
assert!(seg.contains_original(15));
assert!(!seg.contains_original(25));
assert!(seg.contains_expanded(35));
assert!(!seg.contains_expanded(50));
}
#[test]
fn test_generated_region_helpers() {
let region = GeneratedRegion::new(10, 25, "Test::method");
assert_eq!(region.len(), 15);
assert!(!region.is_empty());
assert!(region.contains(15));
assert!(!region.contains(5));
assert!(!region.contains(25));
}
}