use std::{cell::RefCell, rc::Rc};
use crate::chunk::Chunk;
use crate::result::Result;
static SOURCE_INDEX: u8 = 0;
pub type Segment = Vec<i64>;
pub type Line = Vec<Segment>;
pub type Mappings = Vec<Line>;
#[derive(Debug)]
pub struct Mapping {
generated_code_line: u32,
generated_code_column: u32,
hires: bool,
absolute_mappings: Mappings,
}
impl Mapping {
pub fn new(hires: bool) -> Self {
Self {
hires,
generated_code_line: 0,
generated_code_column: 0,
absolute_mappings: vec![],
}
}
pub fn add_chunk(
&mut self,
chunk: Rc<RefCell<Chunk>>,
(original_line, original_column): (u32, u32),
) {
let chunk_content_edited = chunk.borrow().is_content_edited();
self.advance(chunk.borrow().intro.as_str());
if chunk_content_edited {
let content_str = chunk.borrow().content.to_owned();
let content_lines = content_str.split('\n').collect::<Vec<_>>();
for (index, &s) in content_lines.iter().enumerate() {
if !s.is_empty() {
let segment: Vec<i64> = vec![
self.generated_code_column.into(),
SOURCE_INDEX.into(),
original_line.into(),
original_column.into(),
];
if let Some(line) = self
.absolute_mappings
.get_mut(self.generated_code_line as usize)
{
line.push(segment)
} else {
self.absolute_mappings.push(vec![segment])
}
}
if index != content_lines.len() - 1 {
self.generated_code_line += 1;
self.generated_code_column = 0;
} else {
self.generated_code_column += s.len() as u32;
}
}
} else {
let mut original_line = original_line as i64;
let mut original_column = original_column as i64;
let original_str = chunk.borrow().original_str.to_owned();
let mut first = true;
for char in original_str.chars() {
if self.hires || first {
let segment: Segment = vec![
self.generated_code_column.into(),
SOURCE_INDEX.into(),
original_line,
original_column,
];
if let Some(line) = self
.absolute_mappings
.get_mut(self.generated_code_line as usize)
{
line.push(segment)
} else {
self.absolute_mappings.push(vec![segment])
}
}
match char {
'\n' => {
original_line += 1;
original_column = 0;
self.generated_code_line += 1;
self.generated_code_column = 0;
first = true;
}
_ => {
original_column += 1;
self.generated_code_column += 1;
first = false;
}
}
}
}
self.advance(chunk.borrow().outro.as_str());
}
pub fn advance(&mut self, str: &str) {
if str.is_empty() {
return;
}
let lines = str.split('\n').collect::<Vec<_>>();
let mut i = lines.len();
while i > 1 {
self.absolute_mappings.push(Vec::default());
self.generated_code_column = 0;
i -= 1;
}
self.generated_code_line += (lines.len() - 1) as u32;
self.generated_code_column += lines.last().unwrap().len() as u32;
}
pub fn get_decoded_mappings(&mut self) -> Mappings {
let mut source_index: i64 = 0;
let mut original_line: i64 = 0;
let mut original_column: i64 = 0;
let decoded_mappings = self
.absolute_mappings
.iter()
.map(|line| {
let mut generated_column: i64 = 0;
line
.iter()
.map(|segment| {
let generated_column_offset = segment[0] - generated_column;
let source_index_offset = segment[1] - source_index;
let original_line_offset = segment[2] - original_line;
let original_column_offset = segment[3] - original_column;
generated_column = segment[0];
source_index = segment[1];
original_line = segment[2];
original_column = segment[3];
vec![
generated_column_offset,
source_index_offset,
original_line_offset,
original_column_offset,
]
})
.collect::<Line>()
})
.collect::<Mappings>();
decoded_mappings
}
#[allow(clippy::ptr_arg)]
pub fn generate_encoded_mappings(decoded_mappings: &Mappings) -> Result<String> {
let mut encoded_mappings: Vec<String> = vec![];
for line in decoded_mappings.iter() {
let mut line_str: Vec<String> = vec![];
for segment in line.iter() {
let mut segment_str: Vec<String> = vec![];
for item in segment.iter() {
let mut vlq_output: Vec<u8> = vec![];
vlq::encode(item.to_owned(), &mut vlq_output)?;
segment_str.push(String::from_utf8(vlq_output)?);
}
line_str.push(segment_str.join(""));
}
encoded_mappings.push(line_str.join(","));
}
let encoded_mappings_str = encoded_mappings.join(";");
Ok(encoded_mappings_str)
}
}
#[cfg(test)]
mod tests {
use super::Mapping;
#[test]
fn absolute_mapping_to_relative_mapping() {
let mut mapping = Mapping::new(false);
mapping
.absolute_mappings
.push(vec![vec![3, 1, 0, 1], vec![4, 1, 0, 1]]);
mapping
.absolute_mappings
.push(vec![vec![5, 5, 2, 9], vec![6, 6, 3, 10]]);
let decoded_mappings = mapping.get_decoded_mappings();
assert_eq!(
&decoded_mappings,
&vec![
vec![vec![3, 1, 0, 1], vec![1, 0, 0, 0]],
vec![vec![5, 4, 2, 8], vec![1, 1, 1, 1]],
]
)
}
}