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