use crate::types::{Pos, Span};
pub trait SpanMapping {
fn map_span(&self, new_span: Span) -> Option<Span>;
}
#[derive(Clone, Debug, Default)]
pub struct SpanMap {
pairs: Vec<(Span, Span)>,
}
impl SpanMap {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self { pairs: Vec::new() }
}
pub fn push(&mut self, new_span: Span, old_span: Span) {
self.pairs.push((new_span, old_span));
}
pub fn sort(&mut self) {
self.pairs.sort_by_key(|(s, _)| s.start);
}
#[must_use]
pub fn from_pairs(mut pairs: Vec<(Span, Span)>) -> Self {
pairs.sort_by_key(|(s, _)| s.start);
Self { pairs }
}
}
impl SpanMapping for SpanMap {
fn map_span(&self, new_span: Span) -> Option<Span> {
if self.pairs.is_empty() {
return None;
}
let pos = new_span.start;
let i = match self.pairs.binary_search_by_key(&pos, |(s, _)| s.start) {
Ok(i) => i,
Err(0) => return None,
Err(i) => i - 1,
};
let (new, old) = &self.pairs[i];
if new_span.start >= new.end {
return None;
}
let offset_in_seg = new_span.start - new.start;
let len = (new_span.end - new_span.start).min(new.end - new_span.start);
let old_start = old.start + offset_in_seg.min(old.end.saturating_sub(old.start));
let old_end = (old_start + len).min(old.end);
Some(Span::new(old_start, old_end))
}
}
#[inline]
pub fn map_offset(mapping: &impl SpanMapping, new_offset: Pos) -> Option<Pos> {
mapping
.map_span(Span::new(new_offset, new_offset + 1))
.map(|s| s.start)
}