1use std::sync::Arc;
2
3use anyhow::{bail, Context, Result};
4use serde::{Deserialize, Serialize};
5use sourcemap::{vlq::parse_vlq_segment, RawToken, SourceMap};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(untagged)]
9pub enum SourceMapContent {
10 Json(String),
11 #[serde(rename_all = "camelCase")]
12 Parsed {
13 #[serde(default)]
14 sources: Vec<Arc<str>>,
15 #[serde(default)]
16 names: Vec<Arc<str>>,
17 #[serde(default)]
18 mappings: String,
19 #[serde(default)]
20 range_mappings: String,
21 #[serde(default)]
22 file: Option<Arc<str>>,
23 #[serde(default)]
24 source_root: Option<String>,
25 #[serde(default)]
26 sources_content: Option<Vec<Option<Arc<str>>>>,
27 },
28}
29
30impl SourceMapContent {
31 pub fn to_sourcemap(&self) -> Result<SourceMap> {
32 match self {
33 SourceMapContent::Json(s) => {
34 SourceMap::from_slice(s.as_bytes()).context("failed to parse sourcemap")
35 }
36 SourceMapContent::Parsed {
37 sources,
38 names,
39 mappings,
40 range_mappings: _,
41 file,
42 source_root,
43 sources_content,
44 } => {
45 let mut dst_col;
46 let mut src_id = 0;
47 let mut src_line = 0;
48 let mut src_col = 0;
49 let mut name_id = 0;
50
51 let allocation_size = mappings.matches(&[',', ';'][..]).count() + 10;
52 let mut tokens = Vec::with_capacity(allocation_size);
53
54 let mut nums = Vec::with_capacity(6);
55
56 for (dst_line, line) in mappings.split(';').enumerate() {
57 if line.is_empty() {
58 continue;
59 }
60
61 dst_col = 0;
62
63 for segment in line.split(',') {
64 if segment.is_empty() {
65 continue;
66 }
67
68 nums.clear();
69 nums = parse_vlq_segment(segment)?;
70 dst_col = (i64::from(dst_col) + nums[0]) as u32;
71
72 let mut src = !0;
73 let mut name = !0;
74
75 if nums.len() > 1 {
76 if nums.len() != 4 && nums.len() != 5 {
77 bail!(
78 "invalid vlq segment size; expected 4 or 5, got {}",
79 nums.len()
80 );
81 }
82 src_id = (i64::from(src_id) + nums[1]) as u32;
83 if src_id >= sources.len() as u32 {
84 bail!("invalid source reference: {}", src_id);
85 }
86
87 src = src_id;
88 src_line = (i64::from(src_line) + nums[2]) as u32;
89 src_col = (i64::from(src_col) + nums[3]) as u32;
90
91 if nums.len() > 4 {
92 name_id = (i64::from(name_id) + nums[4]) as u32;
93 if name_id >= names.len() as u32 {
94 bail!("invalid name reference: {}", name_id);
95 }
96 name = name_id;
97 }
98 }
99
100 tokens.push(RawToken {
101 dst_line: dst_line as u32,
102 dst_col,
103 src_line,
104 src_col,
105 src_id: src,
106 name_id: name,
107 is_range: false,
108 });
109 }
110 }
111
112 let mut map = SourceMap::new(
113 file.clone(),
114 tokens,
115 names.clone(),
116 sources.clone(),
117 sources_content.clone(),
118 );
119 map.set_source_root(source_root.clone());
120 Ok(map)
121 }
122 }
123 }
124}