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