1use std::path::{Path, PathBuf};
8
9use crate::ast::Program;
10use crate::error::{RexxDiagnostic, RexxError, RexxResult};
11use crate::lexer::Lexer;
12use crate::parser::Parser;
13
14pub fn resolve_external(
26 name: &str,
27 source_dir: Option<&Path>,
28) -> RexxResult<Option<(Program, PathBuf)>> {
29 let lower = name.to_lowercase();
30 let upper = name.to_uppercase();
31
32 let mut candidates: Vec<String> = vec![format!("{lower}.rexx"), format!("{lower}.rex")];
33 if upper != lower {
34 candidates.push(format!("{upper}.rexx"));
35 candidates.push(format!("{upper}.rex"));
36 }
37
38 let mut search_dirs: Vec<PathBuf> = Vec::new();
40 if let Some(dir) = source_dir {
41 search_dirs.push(dir.to_path_buf());
42 }
43 if let Ok(rexxpath) = std::env::var("REXXPATH") {
44 for entry in std::env::split_paths(&rexxpath) {
45 if entry.is_dir() {
46 search_dirs.push(entry);
47 }
48 }
49 }
50 if let Ok(cwd) = std::env::current_dir() {
51 search_dirs.push(cwd);
52 }
53
54 for dir in &search_dirs {
55 for candidate in &candidates {
56 let full = dir.join(candidate);
57 if full.is_file() {
58 let source = std::fs::read_to_string(&full).map_err(|e| {
59 RexxDiagnostic::new(RexxError::SystemFailure)
60 .with_detail(format!("cannot read '{}': {e}", full.display()))
61 })?;
62 let mut lexer = Lexer::new(&source);
63 let tokens = lexer.tokenize()?;
64 let mut parser = Parser::new(tokens);
65 let program = parser.parse()?;
66 let canonical = full.canonicalize().unwrap_or(full);
67 return Ok(Some((program, canonical)));
68 }
69 }
70 }
71
72 Ok(None)
73}