1use crate::ast::ProgramNode;
2pub use crate::errors::Result;
3pub use crate::errors::TranslatorError;
4use crate::generate_ts::GenerateTs;
5use crate::line_numbers::LineMarks;
6use arcstr::ArcStr;
7use lru::LruCache;
8use oxc_sourcemap::{SourceMap, SourceMapBuilder};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::mem::take;
12use std::num::NonZeroUsize;
13
14pub mod rs_translate;
15pub mod ast;
17mod errors;
18mod generate_ts;
19mod line_numbers;
20
21pub type SourceId = ArcStr;
23
24#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
25#[serde(rename_all = "kebab-case", tag = "type")]
26pub enum SourceSpan {
27 Offset { x1: u64, x2: u64 },
28 LineColumn { x1: u32, y1: u32, x2: u32, y2: u32 },
29}
30
31pub struct TsGenerator {
33 sm_builder: SourceMapBuilder,
35 gc_buffer: String,
37 current_line: u32,
39 current_column: u32,
41 next_id: u32,
43 file_map: HashMap<ArcStr, FileInfo>,
44 file_cache: LruCache<ArcStr, FileProvider>,
45 indent_level: u32,
47 indent_text: &'static str,
49}
50
51pub struct FileInfo {
52 id: u32,
54 }
56
57#[derive(Clone)] pub struct FileProvider {
59 content: String, line_marks: LineMarks,
61}
62
63impl TsGenerator {
64 pub fn new() -> Self {
65 Self {
66 sm_builder: Default::default(),
67 gc_buffer: String::new(),
68 current_line: 0,
69 current_column: 0,
70 next_id: 0,
71 file_map: HashMap::new(),
72 file_cache: LruCache::new(NonZeroUsize::new(100).unwrap()), indent_level: 0,
74 indent_text: " ", }
76 }
77}
78
79impl TsGenerator {
80 pub fn generate(&mut self, node: &ProgramNode) -> Result<(String, SourceMap)> {
83 node.generate_ts(self)?;
84 let code = take(&mut self.gc_buffer);
85 let map = take(&mut self.sm_builder);
86 Ok((code, map.into_sourcemap()))
87 }
88
89 fn get_file(&mut self, source_id: &ArcStr) -> Result<FileProvider> {
92 match self.file_cache.get(source_id) {
93 Some(s) => Ok(s.clone()),
94 None => {
95 let content = std::fs::read_to_string(source_id.as_str())?;
96 let line_positions = LineMarks::from(content.as_str());
97 let provider = FileProvider {
98 content,
99 line_marks: line_positions.clone(),
100 };
101 self.file_cache.put(source_id.clone(), provider.clone());
102 Ok(provider)
103 }
104 }
105 }
106
107 fn get_content(&mut self, source_id: &ArcStr) -> Result<String> {
108 match self.file_cache.get(source_id) {
109 Some(s) => Ok(s.content.clone()),
110 None => {
111 let content = std::fs::read_to_string(source_id.as_str())?;
112 let line_positions = LineMarks::from(content.as_str());
113 let provider = FileProvider {
114 content: content.clone(),
115 line_marks: line_positions.clone(),
116 };
117 self.file_cache.put(source_id.clone(), provider.clone());
118 Ok(content)
119 }
120 }
121 }
122
123 fn get_file_id(&mut self, source_id: &ArcStr) -> Result<u32> {
124 match self.file_map.get(source_id) {
125 Some(file_info) => Ok(file_info.id),
126 None => {
127 let id = self.next_id;
128 self.next_id += 1;
129 self.file_map.insert(source_id.clone(), FileInfo { id });
130 Ok(id)
131 }
132 }
133 }
134
135 fn append_mapping(&mut self, code: &str, span: SourceSpan, source_id: &SourceId) -> Result<()> {
137 let file = self.get_file(source_id)?;
138 let (src_line, src_column) = match span {
139 SourceSpan::Offset { x1, .. } => {
140 let line = file.line_marks.from_offset(x1 as usize);
141 (line.0.line, line.1 as u32)
142 }
143 SourceSpan::LineColumn { x1, y1, .. } => (x1, y1),
144 };
145 let file_id = self.get_file_id(source_id)?;
146 self.sm_builder.add_name(code);
147 self.sm_builder
148 .add_source_and_content(source_id.as_str(), &file.content);
149 self.sm_builder.add_token(
150 self.current_line,
151 self.current_column,
152 src_line,
153 src_column,
154 Some(file_id),
155 None,
156 );
157 self.append_text(code);
158 Ok(())
159 }
160
161 fn append_text(&mut self, code: &str) {
163 self.gc_buffer.push_str(code);
164 for c in code.chars() {
165 if c == '\n' {
166 self.current_line += 1;
167 self.current_column = 0;
168 } else {
169 self.current_column += 1;
170 }
171 }
172 }
173
174 pub fn indent(&mut self, text: &str) {
175 self.append_text(text);
176 self.indent_level += 1;
177 self.append_newline()
178 }
179 pub fn dedent(&mut self, text: &str) {
180 self.append_text(text);
181 self.indent_level = self.indent_level.saturating_sub(1);
182 self.append_newline();
183 }
184 pub fn append_newline(&mut self) {
185 self.append_text("\n");
186 for _ in 0..self.indent_level {
187 self.append_text(self.indent_text);
188 }
189 }
190}