use napi_derive::napi;
use crate::api_types::{
GeneratedRegionResult, MappingSegmentResult, SourceMappingResult, SpanResult,
};
#[napi(js_name = "PositionMapper")]
pub struct NativePositionMapper {
segments: Vec<MappingSegmentResult>,
generated_regions: Vec<GeneratedRegionResult>,
}
#[napi(js_name = "NativeMapper")]
pub struct NativeMapper {
pub(crate) inner: NativePositionMapper,
}
#[napi]
impl NativePositionMapper {
#[napi(constructor)]
pub fn new(mapping: SourceMappingResult) -> Self {
Self {
segments: mapping.segments,
generated_regions: mapping.generated_regions,
}
}
#[napi(js_name = "isEmpty")]
pub fn is_empty(&self) -> bool {
self.segments.is_empty() && self.generated_regions.is_empty()
}
#[napi]
pub fn original_to_expanded(&self, pos: u32) -> u32 {
let idx = self.segments.partition_point(|seg| seg.original_end <= pos);
if let Some(seg) = self.segments.get(idx) {
if pos >= seg.original_start && pos < seg.original_end {
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
}
#[napi]
pub fn expanded_to_original(&self, pos: u32) -> Option<u32> {
if self.is_in_generated(pos) {
return None;
}
let idx = self.segments.partition_point(|seg| seg.expanded_end <= pos);
if let Some(seg) = self.segments.get(idx)
&& pos >= seg.expanded_start
&& pos < seg.expanded_end
{
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
}
#[napi]
pub fn generated_by(&self, pos: u32) -> Option<String> {
self.generated_regions
.iter()
.find(|r| pos >= r.start && pos < r.end)
.map(|r| r.source_macro.clone())
}
#[napi]
pub fn map_span_to_original(&self, start: u32, length: u32) -> Option<SpanResult> {
let end = start.saturating_add(length);
let original_start = self.expanded_to_original(start)?;
let original_end = self.expanded_to_original(end)?;
Some(SpanResult {
start: original_start,
length: original_end.saturating_sub(original_start),
})
}
#[napi]
pub fn map_span_to_expanded(&self, start: u32, length: u32) -> SpanResult {
let end = start.saturating_add(length);
let expanded_start = self.original_to_expanded(start);
let expanded_end = self.original_to_expanded(end);
SpanResult {
start: expanded_start,
length: expanded_end.saturating_sub(expanded_start),
}
}
#[napi]
pub fn is_in_generated(&self, pos: u32) -> bool {
self.generated_regions
.iter()
.any(|r| pos >= r.start && pos < r.end)
}
}
#[napi]
impl NativeMapper {
#[napi(constructor)]
pub fn new(mapping: SourceMappingResult) -> Self {
Self {
inner: NativePositionMapper::new(mapping),
}
}
#[napi(js_name = "isEmpty")]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[napi]
pub fn original_to_expanded(&self, pos: u32) -> u32 {
self.inner.original_to_expanded(pos)
}
#[napi]
pub fn expanded_to_original(&self, pos: u32) -> Option<u32> {
self.inner.expanded_to_original(pos)
}
#[napi]
pub fn generated_by(&self, pos: u32) -> Option<String> {
self.inner.generated_by(pos)
}
#[napi]
pub fn map_span_to_original(&self, start: u32, length: u32) -> Option<SpanResult> {
self.inner.map_span_to_original(start, length)
}
#[napi]
pub fn map_span_to_expanded(&self, start: u32, length: u32) -> SpanResult {
self.inner.map_span_to_expanded(start, length)
}
#[napi]
pub fn is_in_generated(&self, pos: u32) -> bool {
self.inner.is_in_generated(pos)
}
}