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