1pub mod prelude;
6use dirs::home_dir;
7use seq_map::SeqMap;
8use std::collections::HashSet;
9use std::io::ErrorKind;
10use std::path::{Path, PathBuf};
11use std::time::Instant;
12use std::{env, io};
13use swamp_script_ast::Function;
14use swamp_script_ast::prelude::*;
15use swamp_script_parser::{AstParser, SpecificError};
16use swamp_script_source_map::{FileId, SourceMap};
17use tracing::debug;
18
19pub struct ParseRoot;
20
21#[derive(Debug)]
22pub enum ParseRootError {
23 ParserError(ParserError),
24}
25
26#[derive(Debug)]
27pub struct ParsedAstModule {
28 pub ast_module: swamp_script_ast::Module,
29 pub file_id: FileId,
30}
31
32impl ParsedAstModule {
33 pub fn declare_external_function(
35 &mut self,
36 parameters: Vec<Parameter>,
37 return_type: Option<Type>,
38 ) {
39 let fake_identifier = Node::default();
40
41 let signature = FunctionDeclaration {
42 name: fake_identifier.clone(),
43 params: parameters,
44 self_parameter: None,
45 return_type,
46 };
47 let external_signature = Function::External(signature);
48
49 self.ast_module.definitions.insert(
50 0, Definition::FunctionDef(external_signature),
52 );
53 }
54}
55
56#[derive(Debug)]
57pub struct RelativePath(pub String);
58
59#[derive(Debug)]
60pub struct ParserError {
61 pub node: Node,
62 pub specific: SpecificError,
63 pub file_id: FileId,
64}
65
66impl ParseRoot {
67 pub fn new() -> Self {
68 Self {}
69 }
70
71 pub fn parse(
72 &self,
73 contents: String,
74 file_id: FileId,
75 ) -> Result<ParsedAstModule, ParseRootError> {
76 let before = Instant::now();
77 let ast_program = AstParser.parse_module(&contents).map_err(|err| {
78 let new_err = ParserError {
79 node: Node { span: err.span },
80 specific: err.specific,
81 file_id,
82 };
83 ParseRootError::ParserError(new_err)
84 })?;
85 let after = Instant::now();
86
87 Ok(ParsedAstModule {
88 ast_module: ast_program,
89 file_id,
90 })
91 }
92}
93
94#[derive(Clone)]
95#[allow(unused)]
96pub struct ModuleInfo {
97 path: Vec<String>,
98 imports: Vec<Vec<String>>,
99 uses: Vec<Vec<String>>,
100 parsed: bool,
101 analyzed: bool,
102}
103
104pub struct DependencyParser {
105 pub import_scanned_modules: SeqMap<Vec<String>, ModuleInfo>,
106 already_parsed_modules: SeqMap<Vec<String>, ParsedAstModule>,
107 pub already_resolved_modules: HashSet<Vec<String>>,
108}
109
110impl Default for DependencyParser {
111 fn default() -> Self {
112 Self::new()
113 }
114}
115
116impl DependencyParser {
117 pub fn new() -> Self {
118 Self {
119 import_scanned_modules: SeqMap::new(),
120 already_parsed_modules: SeqMap::new(),
121 already_resolved_modules: HashSet::new(),
122 }
123 }
124
125 pub fn add_resolved_module(&mut self, module_path: Vec<String>) {
126 self.already_resolved_modules.insert(module_path);
127 }
128
129 pub fn add_ast_module(&mut self, module_path: Vec<String>, parsed_module: ParsedAstModule) {
130 debug!(
131 "Adding ast module parsed outside of graph resolver {:?}",
132 module_path
133 );
134 self.already_parsed_modules
135 .insert(module_path, parsed_module)
136 .expect("can not add parsed module")
137 }
138}
139
140#[derive(Debug)]
141pub enum DependencyError {
142 CircularDependency(Vec<String>),
143 ParseRootError(ParseRootError),
144 ReadFileError(io::Error),
145}
146
147impl From<ParseRootError> for DependencyError {
148 fn from(err: ParseRootError) -> Self {
149 Self::ParseRootError(err)
150 }
151}
152
153pub const LOCAL_ROOT_PACKAGE_PATH: &str = "crate";
154
155pub fn get_all_local_paths(
156 source_map: &SourceMap,
157 parsed_module: &ParsedAstModule,
158) -> (Vec<Vec<String>>, Vec<Vec<String>>) {
159 let mut imports = vec![];
160 let mut uses = vec![];
161
162 for def in parsed_module.ast_module.definitions() {
163 match def {
164 Definition::Mod(import) => {
165 let mut sections = Vec::new();
166 sections.push(LOCAL_ROOT_PACKAGE_PATH.to_string());
167 for section_node in &import.module_path.0 {
168 let import_path = source_map
169 .get_span_source(
170 parsed_module.file_id,
171 section_node.span.offset as usize,
172 section_node.span.length.into(),
173 )
174 .to_string();
175 sections.push(import_path);
176 }
177
178 imports.push(sections);
179 }
180
181 Definition::Use(import) => {
182 let mut sections = Vec::new();
183 for section_node in &import.module_path.0 {
184 let import_path = source_map
185 .get_span_source(
186 parsed_module.file_id,
187 section_node.span.offset as usize,
188 section_node.span.length.into(),
189 )
190 .to_string();
191 sections.push(import_path);
192 }
193 uses.push(sections);
194 }
195 _ => continue,
196 }
197 }
198
199 (imports, uses)
200}
201
202pub fn module_path_to_relative_swamp_file(module_path_vec: &[String]) -> PathBuf {
203 let mut path_buf = PathBuf::new();
204
205 let orig_len = module_path_vec.len();
206
207 let converted_path = if module_path_vec[0] == "crate" {
208 &module_path_vec[1..]
209 } else {
210 module_path_vec
211 };
212
213 path_buf.push(converted_path.join("/"));
214 if orig_len == 1 {
215 path_buf.push("lib"); }
217
218 path_buf.set_extension("swamp");
219
220 path_buf
221}
222
223pub fn module_path_to_relative_swamp_file_string(module_path_vec: &[String]) -> String {
224 module_path_to_relative_swamp_file(module_path_vec)
225 .to_str()
226 .unwrap()
227 .into()
228}
229
230pub fn mount_name_from_path(path: &[String]) -> &str {
231 if path[0] == "crate" {
232 "crate"
233 } else {
234 "registry"
235 }
236}
237
238pub fn parse_single_module(
241 source_map: &mut SourceMap,
242 module_path: &[String],
243) -> Result<ParsedAstModule, DependencyError> {
244 let mount_name = mount_name_from_path(module_path);
245
246 let (file_id, script) = source_map
247 .read_file_relative(
248 mount_name,
249 &module_path_to_relative_swamp_file_string(module_path),
250 )
251 .map_err(DependencyError::ReadFileError)?;
252
253 let parse_module = ParseRoot.parse(script, file_id)?;
254
255 Ok(parse_module)
256}
257
258impl DependencyParser {
259 pub fn parse_local_modules(
260 &mut self,
261 module_path: &[String],
262 source_map: &mut SourceMap,
263 ) -> Result<(), DependencyError> {
264 let mut to_parse = vec![module_path.to_vec()];
265
266 while let Some(path) = to_parse.pop() {
267 let module_path_vec = &path.clone();
268 if self.import_scanned_modules.contains_key(module_path_vec) {
269 continue;
270 }
271
272 let parsed_module_to_scan =
273 if let Some(parsed_module) = self.already_parsed_modules.get(module_path_vec) {
274 parsed_module
275 } else if self.already_resolved_modules.contains(module_path_vec) {
276 continue;
277 } else if path == ["core"] {
278 continue;
279 } else {
280 let parsed_ast_module = parse_single_module(source_map, &path)?;
281
282 self.already_parsed_modules
283 .insert(path.clone(), parsed_ast_module)
284 .expect("TODO: panic message");
285
286 self.already_parsed_modules
287 .get(&path.clone())
288 .expect("we just inserted it")
289 };
290
291 let (imports, uses) = get_all_local_paths(source_map, parsed_module_to_scan);
292 let filtered_imports: Vec<Vec<String>> = imports
293 .into_iter()
294 .filter(|import| !self.already_resolved_modules.contains(import))
295 .collect();
296
297 let filtered_uses: Vec<Vec<String>> = uses
298 .into_iter()
299 .filter(|import| !self.already_resolved_modules.contains(import))
300 .collect();
301
302 self.import_scanned_modules
303 .insert(
304 path.clone(),
305 ModuleInfo {
306 path: path.clone(),
307 imports: filtered_imports.clone(),
308 uses: filtered_uses.clone(),
309 parsed: false,
310 analyzed: false,
311 },
312 )
313 .expect("TODO: panic message");
314
315 to_parse.extend(filtered_imports.clone());
316
317 to_parse.extend(filtered_uses.clone());
318 }
319 Ok(())
320 }
321
322 pub fn get_parsed_module(&self, path: &[String]) -> Option<&ParsedAstModule> {
323 self.already_parsed_modules.get(&path.to_vec())
324 }
325
326 pub fn get_parsed_module_mut(&mut self, path: &[String]) -> Option<&mut ParsedAstModule> {
327 self.already_parsed_modules.get_mut(&path.to_vec())
328 }
329
330 pub(crate) fn get_analysis_order(&self) -> Result<Vec<Vec<String>>, DependencyError> {
331 let mut order = Vec::new();
332 let mut visited = HashSet::new();
333 let mut temp_visited = HashSet::new();
334
335 fn visit(
336 graph: &DependencyParser,
337 path: &[String],
338 visited: &mut HashSet<Vec<String>>,
339 temp_visited: &mut HashSet<Vec<String>>,
340 order: &mut Vec<Vec<String>>,
341 ) -> Result<(), DependencyError> {
342 if temp_visited.contains(path) {
343 return Err(DependencyError::CircularDependency(Vec::from(path)));
344 }
345
346 if visited.contains(path) {
347 return Ok(());
348 }
349
350 temp_visited.insert(Vec::from(path));
351
352 if let Some(module) = graph.import_scanned_modules.get(&path.to_vec()) {
353 for import in &module.uses {
354 visit(graph, import, visited, temp_visited, order)?;
355 }
356 for import in &module.imports {
357 visit(graph, import, visited, temp_visited, order)?;
358 }
359 }
360
361 order.push(Vec::from(path));
362 visited.insert(Vec::from(path));
363
364 temp_visited.remove(path);
365
366 Ok(())
367 }
368
369 for path in self.import_scanned_modules.keys() {
370 if !visited.contains(path) {
371 visit(self, path, &mut visited, &mut temp_visited, &mut order)?;
372 }
373 }
374
375 Ok(order)
376 }
377}
378
379fn get_current_dir() -> Result<PathBuf, std::io::Error> {
380 let path = env::current_dir()?;
381
382 Ok(path)
385}
386
387#[derive(Debug)]
388pub enum DepLoaderError {
389 DependencyError(DependencyError),
390}
391
392impl From<DependencyError> for DepLoaderError {
393 fn from(e: DependencyError) -> Self {
394 Self::DependencyError(e)
395 }
396}
397
398pub fn os_home_relative_path(project_name: &str) -> io::Result<PathBuf> {
401 home_dir().map_or_else(
402 || {
403 Err(io::Error::new(
404 io::ErrorKind::Other,
405 "Could not determine home directory",
406 ))
407 },
408 |home_path| {
409 let mut path = home_path;
410 path.push(format!(".{project_name}"));
411 Ok(path)
412 },
413 )
414}
415
416pub fn path_from_environment_variable() -> io::Result<PathBuf> {
417 if let Ok(string_value) = &env::var("SWAMP_HOME") {
418 Ok(Path::new(string_value).to_path_buf())
419 } else {
420 Err(io::Error::new(ErrorKind::InvalidData, "missing SWAMP_HOME"))
421 }
422}
423pub fn swamp_home() -> io::Result<PathBuf> {
424 if let Ok(found_path) = path_from_environment_variable() {
425 Ok(found_path)
426 } else {
427 os_home_relative_path("swamp")
428 }
429}
430
431pub fn swamp_registry_path() -> io::Result<PathBuf> {
434 let mut swamp_home = swamp_home()?;
435 swamp_home.push("packages");
436 Ok(swamp_home)
437}
438
439pub fn parse_local_modules_and_get_order(
440 module_path: Vec<String>,
441 dependency_parser: &mut DependencyParser,
442 source_map: &mut SourceMap,
443) -> Result<Vec<Vec<String>>, DepLoaderError> {
444 debug!(current_directory=?get_current_dir().expect("failed to get current directory"), "current directory");
445 dependency_parser.parse_local_modules(&module_path, source_map)?;
446
447 let module_paths_in_order = dependency_parser.get_analysis_order()?;
448
449 Ok(module_paths_in_order)
450}