1use std::{env, path::PathBuf, io::ErrorKind, fmt::Debug};
2use super::*;
3
4pub type ModuleResolveResult = Result<RantProgram, ModuleResolveError>;
6
7pub trait ModuleResolver: Debug {
12 fn try_resolve(&self, context: &mut Rant, module_path: &str, dependant: Option<&RantProgramInfo>) -> ModuleResolveResult;
13}
14
15#[derive(Debug)]
23pub struct DefaultModuleResolver {
24 pub enable_global_modules: bool,
26 pub local_modules_path: Option<String>,
29}
30
31impl DefaultModuleResolver {
32 pub const ENV_MODULES_PATH_KEY: &'static str = "RANT_MODULES_PATH";
34}
35
36impl Default for DefaultModuleResolver {
37 fn default() -> Self {
38 Self {
39 enable_global_modules: true,
40 local_modules_path: None,
41 }
42 }
43}
44
45impl ModuleResolver for DefaultModuleResolver {
46 fn try_resolve(&self, context: &mut Rant, module_path: &str, dependant: Option<&RantProgramInfo>) -> ModuleResolveResult {
47
48 if let Some(full_module_path) = self.find_module_path(module_path, dependant) {
50 let mut errors = vec![];
51 let compile_result = context.compile_file(full_module_path, &mut errors);
52 match compile_result {
53 Ok(module) => Ok(module),
54 Err(err) => {
55 Err(ModuleResolveError {
56 name: module_path.to_owned(),
57 reason: match err{
58 CompilerError::SyntaxError => {
59 ModuleResolveErrorReason::CompileFailed(errors)
60 },
61 CompilerError::IOError(ioerr) => {
62 match ioerr {
63 IOErrorKind::NotFound => {
64 ModuleResolveErrorReason::NotFound
65 },
66 _ => ModuleResolveErrorReason::FileIOError(ioerr)
67 }
68 }
69 }
70 })
71 }
72 }
73 } else {
74 Err(ModuleResolveError {
75 name: module_path.to_owned(),
76 reason: ModuleResolveErrorReason::NotFound,
77 })
78 }
79 }
80}
81
82impl DefaultModuleResolver {
83 #[inline]
84 fn find_module_path(&self, module_path: &str, dependant: Option<&RantProgramInfo>) -> Option<PathBuf> {
85
86 let module_path = PathBuf::from(
87 module_path.replace("/", &String::from(std::path::MAIN_SEPARATOR))
88 )
89 .with_extension(RANT_FILE_EXTENSION);
90
91 macro_rules! search_for_module {
92 ($path:expr) => {
93 let path = $path;
94 if let Ok(full_module_path) = path
96 .join(&module_path)
97 .canonicalize()
98 {
99 if full_module_path.starts_with(path)
101 && full_module_path.exists()
102 {
103 return Some(full_module_path)
104 }
105 }
106 }
107 }
108
109 if let Some(dependant_path) = dependant.map(|d| d.path.as_deref()) {
111 if let Some(program_path) =
112 dependant_path
113 .map(PathBuf::from)
114 .as_deref()
115 .and_then(|p| p.parent())
116 {
117 search_for_module!(program_path);
118 }
119 }
120
121 if let Some(local_modules_path) =
123 self.local_modules_path
124 .as_ref()
125 .map(PathBuf::from)
126 .or_else(||
127 env::current_dir()
128 .ok()
129 )
130 .and_then(|p| p.canonicalize().ok())
131 {
132 search_for_module!(local_modules_path);
133 }
134
135 if self.enable_global_modules {
137 if let Some(global_modules_path) =
138 env::var_os(Self::ENV_MODULES_PATH_KEY)
139 .map(PathBuf::from)
140 .and_then(|p| p.canonicalize().ok())
141 {
142 search_for_module!(global_modules_path);
143 }
144 }
145
146 None
147 }
148}
149
150#[derive(Debug)]
154pub struct NoModuleResolver;
155
156impl ModuleResolver for NoModuleResolver {
157 fn try_resolve(&self, _context: &mut Rant, module_path: &str, _dependant: Option<&RantProgramInfo>) -> ModuleResolveResult {
158 Err(ModuleResolveError {
159 name: module_path.to_owned(),
160 reason: ModuleResolveErrorReason::NotFound,
161 })
162 }
163}
164
165#[derive(Debug)]
167pub struct ModuleResolveError {
168 pub name: String,
169 pub reason: ModuleResolveErrorReason,
170}
171
172impl Error for ModuleResolveError {}
173
174impl ModuleResolveError {
175 #[inline]
177 pub fn name(&self) -> &str {
178 &self.name
179 }
180
181 #[inline]
183 pub fn reason(&self) -> &ModuleResolveErrorReason {
184 &self.reason
185 }
186}
187
188#[derive(Debug)]
190pub enum ModuleResolveErrorReason {
191 NotFound,
193 CompileFailed(Vec<CompilerMessage>),
195 FileIOError(ErrorKind),
197}
198
199impl Display for ModuleResolveError {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 match self.reason() {
202 ModuleResolveErrorReason::NotFound => write!(f, "module '{}' not found", self.name()),
203 ModuleResolveErrorReason::CompileFailed(msgs) => write!(f, "module '{}' failed to compile: {}",
204 self.name(),
205 msgs.iter().fold(String::new(), |mut acc, msg| {
206 acc.push_str(&format!("[{}] {}\n", msg.severity(), msg.message()));
207 acc
208 })),
209 ModuleResolveErrorReason::FileIOError(ioerr) => write!(f, "file I/O error ({:?})", ioerr),
210 }
211 }
212}
213
214impl IntoRuntimeResult<RantProgram> for ModuleResolveResult {
215 fn into_runtime_result(self) -> RuntimeResult<RantProgram> {
216 self.map_err(|err| RuntimeError {
217 error_type: RuntimeErrorType::ModuleError(err),
218 description: None,
219 stack_trace: None,
220 })
221 }
222}