use crate::engine::source::Span;
#[derive(Debug, Clone, Copy)]
struct ProvSeg {
flat_start: u32,
src_start: u32,
}
#[derive(Debug, Default)]
pub(crate) struct ProvMap {
segs: Vec<ProvSeg>,
flat_len: u32,
src_len: u32,
}
impl ProvMap {
pub(crate) fn new(src_len: u32) -> Self {
Self {
segs: Vec::new(),
flat_len: 0,
src_len,
}
}
pub(crate) fn push_text(&mut self, flat_start: u32, span: Span) {
self.segs.push(ProvSeg {
flat_start,
src_start: span.start,
});
}
pub(crate) fn seal(&mut self, flat_len: u32) {
self.flat_len = flat_len;
}
pub(crate) fn src_len(&self) -> u32 {
self.src_len
}
pub(crate) fn source_offset(&self, flat_off: u32) -> u32 {
if flat_off >= self.flat_len {
return self.src_len;
}
match self.segs.partition_point(|s| s.flat_start <= flat_off) {
0 => 0,
n => {
let seg = self.segs[n - 1];
seg.src_start + (flat_off - seg.flat_start)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::source::{SourceId, Span};
fn span(start: u32, end: u32) -> Span {
Span::new(SourceId(0), start, end)
}
#[test]
fn empty_map_maps_everything_to_end() {
let map = ProvMap::new(0);
assert_eq!(map.source_offset(0), 0);
assert_eq!(map.source_offset(5), 0);
}
#[test]
fn a_single_verbatim_run_maps_one_to_one() {
let mut map = ProvMap::new(5);
assert_eq!(map.src_len(), 5);
map.push_text(0, span(0, 5));
map.seal(5);
assert_eq!(map.source_offset(0), 0);
assert_eq!(map.source_offset(3), 3);
assert_eq!(map.source_offset(5), 5);
assert_eq!(map.source_offset(9), 5);
}
#[test]
fn runs_after_a_marker_keep_absolute_source_offsets() {
let mut map = ProvMap::new(9);
map.push_text(0, span(0, 2));
map.push_text(12, span(7, 9));
map.seal(14);
assert_eq!(map.source_offset(1), 1);
assert_eq!(map.source_offset(13), 8);
}
#[test]
fn an_offset_before_the_first_run_maps_to_zero() {
let mut map = ProvMap::new(3);
map.push_text(10, span(7, 10));
map.seal(13);
assert_eq!(map.source_offset(2), 0);
}
}