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 if path == ["core"] {
277 continue;
278 } else {
279 let parsed_ast_module = parse_single_module(source_map, &path)?;
280
281 self.already_parsed_modules
282 .insert(path.clone(), parsed_ast_module)
283 .expect("TODO: panic message");
284
285 self.already_parsed_modules
286 .get(&path.clone())
287 .expect("we just inserted it")
288 };
289
290 let (imports, uses) = get_all_local_paths(source_map, parsed_module_to_scan);
291 let filtered_imports: Vec<Vec<String>> = imports
292 .into_iter()
293 .filter(|import| !self.already_resolved_modules.contains(import))
294 .collect();
295
296 let filtered_uses: Vec<Vec<String>> = uses
297 .into_iter()
298 .filter(|import| !self.already_resolved_modules.contains(import))
299 .collect();
300
301 self.import_scanned_modules
302 .insert(
303 path.clone(),
304 ModuleInfo {
305 path: path.clone(),
306 imports: filtered_imports.clone(),
307 uses: filtered_uses.clone(),
308 parsed: false,
309 analyzed: false,
310 },
311 )
312 .expect("TODO: panic message");
313
314 to_parse.extend(filtered_imports.clone());
315
316 to_parse.extend(filtered_uses.clone());
317 }
318 Ok(())
319 }
320
321 pub fn get_parsed_module(&self, path: &[String]) -> Option<&ParsedAstModule> {
322 self.already_parsed_modules.get(&path.to_vec())
323 }
324
325 pub fn get_parsed_module_mut(&mut self, path: &[String]) -> Option<&mut ParsedAstModule> {
326 self.already_parsed_modules.get_mut(&path.to_vec())
327 }
328
329 pub(crate) fn get_analysis_order(&self) -> Result<Vec<Vec<String>>, DependencyError> {
330 let mut order = Vec::new();
331 let mut visited = HashSet::new();
332 let mut temp_visited = HashSet::new();
333
334 fn visit(
335 graph: &DependencyParser,
336 path: &[String],
337 visited: &mut HashSet<Vec<String>>,
338 temp_visited: &mut HashSet<Vec<String>>,
339 order: &mut Vec<Vec<String>>,
340 ) -> Result<(), DependencyError> {
341 if temp_visited.contains(path) {
342 return Err(DependencyError::CircularDependency(Vec::from(path)));
343 }
344
345 if visited.contains(path) {
346 return Ok(());
347 }
348
349 temp_visited.insert(Vec::from(path));
350
351 if let Some(module) = graph.import_scanned_modules.get(&path.to_vec()) {
352 for import in &module.uses {
353 visit(graph, import, visited, temp_visited, order)?;
354 }
355 for import in &module.imports {
356 visit(graph, import, visited, temp_visited, order)?;
357 }
358 }
359
360 order.push(Vec::from(path));
361 visited.insert(Vec::from(path));
362
363 temp_visited.remove(path);
364
365 Ok(())
366 }
367
368 for path in self.import_scanned_modules.keys() {
369 if !visited.contains(path) {
370 visit(self, path, &mut visited, &mut temp_visited, &mut order)?;
371 }
372 }
373
374 Ok(order)
375 }
376}
377
378fn get_current_dir() -> Result<PathBuf, std::io::Error> {
379 let path = env::current_dir()?;
380
381 Ok(path)
384}
385
386#[derive(Debug)]
387pub enum DepLoaderError {
388 DependencyError(DependencyError),
389}
390
391impl From<DependencyError> for DepLoaderError {
392 fn from(e: DependencyError) -> Self {
393 Self::DependencyError(e)
394 }
395}
396
397pub fn os_home_relative_path(project_name: &str) -> io::Result<PathBuf> {
400 home_dir().map_or_else(
401 || {
402 Err(io::Error::new(
403 io::ErrorKind::Other,
404 "Could not determine home directory",
405 ))
406 },
407 |home_path| {
408 let mut path = home_path;
409 path.push(format!(".{project_name}"));
410 Ok(path)
411 },
412 )
413}
414
415pub fn path_from_environment_variable() -> io::Result<PathBuf> {
416 if let Ok(string_value) = &env::var("SWAMP_HOME") {
417 Ok(Path::new(string_value).to_path_buf())
418 } else {
419 Err(io::Error::new(ErrorKind::InvalidData, "missing SWAMP_HOME"))
420 }
421}
422pub fn swamp_home() -> io::Result<PathBuf> {
423 if let Ok(found_path) = path_from_environment_variable() {
424 Ok(found_path)
425 } else {
426 os_home_relative_path("swamp")
427 }
428}
429
430pub fn swamp_registry_path() -> io::Result<PathBuf> {
433 let mut swamp_home = swamp_home()?;
434 swamp_home.push("packages");
435 Ok(swamp_home)
436}
437
438pub fn parse_local_modules_and_get_order(
439 module_path: Vec<String>,
440 dependency_parser: &mut DependencyParser,
441 source_map: &mut SourceMap,
442) -> Result<Vec<Vec<String>>, DepLoaderError> {
443 debug!(current_directory=?get_current_dir().expect("failed to get current directory"), "current directory");
444 dependency_parser.parse_local_modules(&module_path, source_map)?;
445
446 let module_paths_in_order = dependency_parser.get_analysis_order()?;
447
448 Ok(module_paths_in_order)
449}