1pub type RawMappings = Vec<Vec<Vec<i32>>>;
2pub type Mappings = Vec<Vec<Option<(i32, i32, i32, i32)>>>;
3use std::collections::HashMap;
4
5pub const COMMA_CHAR: char = ',';
6pub const SPACE_CHAR: char = ' ';
7pub const SEMICOLON_CHAR: char = ';';
8pub const VLQ_TABLE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
9
10fn create_lookup_tables() -> (HashMap<char, u8>, Vec<char>) {
11 let chars = VLQ_TABLE;
12 let mut char_to_integer = HashMap::new();
13 let mut integer_to_char = vec![SPACE_CHAR; 65];
14
15 for (i, c) in chars.chars().enumerate() {
16 char_to_integer.insert(c, i as u8);
17 integer_to_char[i] = c;
18 }
19
20 (char_to_integer, integer_to_char)
21}
22
23fn decode(string: &str) -> Vec<i32> {
24 let (char_to_integer, _) = create_lookup_tables();
25 let mut result = Vec::new();
26 let mut shift = 0;
27 let mut value = 0;
28
29 for c in string.chars() {
30 let integer = match char_to_integer.get(&c) {
31 Some(&val) => val as i32,
32 None => continue,
33 };
34
35 let has_continuation_bit = integer & 32;
36 let integer = integer & 31;
37 value += integer << shift;
38
39 if has_continuation_bit != 0 {
40 shift += 5;
41 } else {
42 let should_negate = value & 1;
43 value >>= 1;
44
45 if should_negate != 0 {
46 result.push(if value == 0 { -0x80000000 } else { -value });
47 } else {
48 result.push(value);
49 }
50 value = 0;
51 shift = 0;
52 }
53 }
54 result
55}
56
57pub fn parse(mappings: &str) -> Mappings {
58 let vlqs = mappings
59 .split(SEMICOLON_CHAR)
60 .map(|line| line.split(COMMA_CHAR).map(decode).collect::<Vec<_>>())
61 .collect::<Vec<_>>();
62
63 process(vlqs)
64}
65
66fn process(decoded: RawMappings) -> Mappings {
67 let mut source_file_index = 0;
68 let mut source_code_line = 0;
69 let mut source_code_column = 0;
70
71 decoded
72 .into_iter()
73 .map(|line| {
74 let mut generated_code_column = 0;
75
76 line.into_iter()
77 .map(|segment| {
78 if segment.len() == 0 {
79 return None;
80 }
81 generated_code_column += segment[0];
82
83 source_file_index += segment[1];
84 source_code_line += segment[2];
85 source_code_column += segment[3];
86
87 Some((
88 generated_code_column,
89 source_file_index,
90 source_code_line,
91 source_code_column,
92 ))
93 })
94 .collect()
95 })
96 .collect()
97}
98
99pub fn to_source(mappings: &Mappings, line: usize, column: usize) -> Option<(usize, usize)> {
100 let Some(line_mappings) = mappings.get(line - 1) else {
101 return None;
102 };
103
104 for &segment in line_mappings {
105 let Some((gen_col, _, src_line, src_col)) = segment else {
106 continue;
107 };
108 if gen_col as usize >= column {
109 return Some((((src_line + 1) as usize), src_col as usize));
110 }
111 }
112 None
113}
114
115#[cfg(test)]
116mod tests {
117 use crate::{parse, to_source};
118
119 #[test]
120 fn test_dummy() {
121 let input = ";;;;IAQmB,OAAO,GAAE,MAAM,CAAA;;OARzB,KAAK,MAAA,aAAA,CAAA;OACP,CAAQ,MAAA,8BAAA,CAAA;AAEf,MAAM,MAAM,GAAG,MAAM,CAAC;OAIV,SAAA,MAAA;IAFZ,YAAA,MAAA,EAAA,MAAA,EAAA,cAAA,EAAA,MAAA,GAAA,CAAA,CAAA,EAAA,YAAA,GAAA,SAAA,EAAA,SAAA;;;;;uBAGqC,aAAa,CAAA;;;IAL5B,CAAA;;;;;;;;;;;;;;IAKpB,OAAO,CAAC,QAAQ,CAAU,OAAA,EAAA,MAAM,CAAiB;IAEjD,aAAA;;YACE,GAAG,CAAA,MAAA,EAAA,CAAA;YAAH,GAAG,CAuBF,MAAM,CAAC,MAAM,CAAA,CAAA;;;YAtBZ,MAAM,CAAA,MAAA,EAAA,CAAA;YAAN,MAAM,CAoBL,KAAK,CAAC,MAAM,CAAA,CAAA;;;YAnBX,IAAI,QAAC,UAAU,CAAA,CAAA;YAAf,IAAI,CACD,QAAQ,CAAC,EAAE,CAAA,CAAA;YADd,IAAI,CAED,UAAU,CAAC,UAAU,CAAC,IAAI,CAAA,CAAA;YAF7B,IAAI,CAGD,OAAO,CAAC,GAAG,EAAE;gBACZ,GAAO,CAAA;YACT,CAAC,CAAA,CAAA;;QALH,IAAI,CAAA,GAAA,EAAA,CAAA;;YAMJ,IAAI,QAAC,WAAW,CAAA,CAAA;YAAhB,IAAI,CACD,QAAQ,CAAC,EAAE,CAAA,CAAA;YADd,IAAI,CAED,UAAU,CAAC,UAAU,CAAC,IAAI,CAAA,CAAA;YAF7B,IAAI,CAGD,OAAO,CAAC,GAAG,EAAE;gBACZ,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,8BAA8B,EAAE,EAAS,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACpF,CAAC,CAAA,CAAA;;QALH,IAAI,CAAA,GAAA,EAAA,CAAA;;YAMJ,IAAI,QAAC,WAAW,CAAA,CAAA;YAAhB,IAAI,CACD,QAAQ,CAAC,EAAE,CAAA,CAAA;YADd,IAAI,CAED,UAAU,CAAC,UAAU,CAAC,IAAI,CAAA,CAAA;YAF7B,IAAI,CAGD,OAAO,CAAC,GAAG,EAAE;gBACZ;oBAAO,CAAC;YACV,CAAC,CAAA,CAAA;;QALH,IAAI,CAAA,GAAA,EAAA,CAAA;QAbN,MAAM,CAAA,GAAA,EAAA,CAAA;QADR,GAAG,CAAA,GAAA,EAAA,CAAA;IAwBJ,CAAA;;;;;;;;AAGH;IACE,IAAO,CAAA;AACT,CAAC;AAED;IACE,IAAO,CAAA;AACT,CAAC;AACD;IACE,MAAM,KAAK,CAAC,MAAM,CAAC,CAAA;AACrB,CAAC;";
122 let mappings = parse(input);
123 let target = to_source(&mappings, 54, 1);
124 assert!(target == Some((20, 8)));
125
126 let target = to_source(&mappings, 52, 1);
127 println!("{:?}", target);
128 assert!(target == None);
129
130 let target = to_source(&mappings, 40, 1);
131 println!("{:?}", target);
132 assert!(target == Some((13, 6)));
133 }
134}