moon_config/
config_loader.rs1use crate::config_cache::ConfigCache;
2use crate::config_finder::ConfigFinder;
3use crate::extensions_config::ExtensionsConfig;
4use crate::formats::hcl::HclFormat;
5use crate::inherited_tasks_config::InheritedTasksConfig;
6use crate::inherited_tasks_manager::InheritedTasksManager;
7use crate::project_config::{PartialProjectConfig, ProjectConfig};
8use crate::template_config::TemplateConfig;
9use crate::toolchains_config::ToolchainsConfig;
10use crate::workspace_config::WorkspaceConfig;
11use moon_common::color;
12use schematic::{Config, ConfigLoader as Loader};
13use std::ops::Deref;
14use std::path::{Path, PathBuf};
15
16#[derive(Clone, Debug, Default)]
17pub struct ConfigLoader {
18 pub dir: PathBuf, pub dir_prefix: String,
20 finder: ConfigFinder,
21}
22
23impl ConfigLoader {
24 pub fn new(dir: impl AsRef<Path>) -> Self {
25 Self {
26 dir: dir.as_ref().to_path_buf(),
27 ..Default::default()
28 }
29 }
30
31 pub fn locate_dir(&mut self, workspace_root: &Path) -> PathBuf {
32 let moon_dir = workspace_root.join(".moon");
33 let config_moon_dir = workspace_root.join(".config").join("moon");
34
35 if config_moon_dir.exists() {
36 self.dir = config_moon_dir;
37 self.dir_prefix = ".config/moon".into();
38 } else {
39 self.dir = moon_dir;
40 self.dir_prefix = ".moon".into();
41 }
42
43 self.dir.clone()
44 }
45
46 pub fn create_extensions_loader<P: AsRef<Path>>(
47 &self,
48 workspace_root: P,
49 ) -> miette::Result<Loader<ExtensionsConfig>> {
50 let mut loader = Loader::<ExtensionsConfig>::new();
51
52 loader
53 .set_cacher(ConfigCache::new(&self.dir))
54 .set_help(color::muted_light(
55 "https://moonrepo.dev/docs/config/extensions",
56 ))
57 .set_root(workspace_root);
58
59 self.prepare_loader(&mut loader, self.get_extensions_files())?;
60
61 Ok(loader)
62 }
63
64 pub fn create_project_loader<P: AsRef<Path>>(
65 &self,
66 project_root: P,
67 ) -> miette::Result<Loader<ProjectConfig>> {
68 let project_root = project_root.as_ref();
69 let mut loader = Loader::<ProjectConfig>::new();
70
71 loader.set_help(color::muted_light(
72 "https://moonrepo.dev/docs/config/project",
73 ));
74
75 self.prepare_loader(&mut loader, self.get_project_files(project_root))?;
76
77 Ok(loader)
78 }
79
80 pub fn create_tasks_loader<P: AsRef<Path>>(
81 &self,
82 workspace_root: P,
83 ) -> miette::Result<Loader<InheritedTasksConfig>> {
84 let mut loader = Loader::<InheritedTasksConfig>::new();
85
86 loader
87 .set_cacher(ConfigCache::new(&self.dir))
88 .set_help(color::muted_light("https://moonrepo.dev/docs/config/tasks"))
89 .set_root(workspace_root);
90
91 Ok(loader)
94 }
95
96 pub fn create_template_loader<P: AsRef<Path>>(
97 &self,
98 template_root: P,
99 ) -> miette::Result<Loader<TemplateConfig>> {
100 let template_root = template_root.as_ref();
101 let mut loader = Loader::<TemplateConfig>::new();
102
103 loader.set_help(color::muted_light(
104 "https://moonrepo.dev/docs/config/template",
105 ));
106
107 self.prepare_loader(&mut loader, self.get_template_files(template_root))?;
108
109 Ok(loader)
110 }
111
112 pub fn create_toolchains_loader<P: AsRef<Path>>(
113 &self,
114 workspace_root: P,
115 ) -> miette::Result<Loader<ToolchainsConfig>> {
116 let mut loader = Loader::<ToolchainsConfig>::new();
117
118 loader
119 .set_cacher(ConfigCache::new(&self.dir))
120 .set_help(color::muted_light(
121 "https://moonrepo.dev/docs/config/toolchain",
122 ))
123 .set_root(workspace_root);
124
125 self.prepare_loader(&mut loader, self.get_toolchains_files())?;
126
127 Ok(loader)
128 }
129
130 pub fn create_workspace_loader<P: AsRef<Path>>(
131 &self,
132 workspace_root: P,
133 ) -> miette::Result<Loader<WorkspaceConfig>> {
134 let mut loader = Loader::<WorkspaceConfig>::new();
135
136 loader
137 .set_cacher(ConfigCache::new(&self.dir))
138 .set_help(color::muted_light(
139 "https://moonrepo.dev/docs/config/workspace",
140 ))
141 .set_root(workspace_root);
142
143 self.prepare_loader(&mut loader, self.get_workspace_files())?;
144
145 Ok(loader)
146 }
147
148 pub fn load_extensions_config<P: AsRef<Path>>(
149 &self,
150 workspace_root: P,
151 ) -> miette::Result<ExtensionsConfig> {
152 let mut result = self.create_extensions_loader(workspace_root)?.load()?;
153
154 #[cfg(feature = "proto")]
155 {
156 use proto_core::PluginLocator;
157
158 result.config.inherit_defaults()?;
159
160 for config in result.config.plugins.values_mut() {
162 if let Some(PluginLocator::File(file)) = &mut config.plugin {
163 let file_path = file.get_unresolved_path();
164
165 file.path = Some(if file_path.is_absolute() {
166 file_path
167 } else {
168 self.dir.join(file_path)
169 });
170 }
171 }
172 }
173
174 Ok(result.config)
175 }
176
177 pub fn load_project_config<P: AsRef<Path>>(
178 &self,
179 project_root: P,
180 ) -> miette::Result<ProjectConfig> {
181 let result = self.create_project_loader(project_root)?.load()?;
182
183 Ok(result.config)
184 }
185
186 pub fn load_project_partial_config<P: AsRef<Path>>(
187 &self,
188 project_root: P,
189 ) -> miette::Result<PartialProjectConfig> {
190 let result = self
191 .create_project_loader(project_root)?
192 .load_partial(&())?;
193
194 Ok(result)
195 }
196
197 pub fn load_project_config_from_source<P: AsRef<Path>, S: AsRef<str>>(
198 &self,
199 workspace_root: P,
200 project_source: S,
201 ) -> miette::Result<ProjectConfig> {
202 let workspace_root = workspace_root.as_ref();
203 let project_root = workspace_root.join(project_source.as_ref());
204
205 let result = self
206 .create_project_loader(project_root)?
207 .set_root(workspace_root)
208 .load()?;
209
210 Ok(result.config)
211 }
212
213 pub fn load_tasks_config_from_path<T: AsRef<Path>, P: AsRef<Path>>(
214 &self,
215 workspace_root: T,
216 path: P,
217 ) -> miette::Result<InheritedTasksConfig> {
218 let mut loader = self.create_tasks_loader(workspace_root)?;
219
220 self.prepare_loader(&mut loader, vec![path.as_ref().to_path_buf()])?;
221
222 Ok(loader.load()?.config)
223 }
224
225 pub fn load_tasks_manager<P: AsRef<Path>>(
226 &self,
227 workspace_root: P,
228 ) -> miette::Result<InheritedTasksManager> {
229 self.load_tasks_manager_from(workspace_root, &self.dir)
230 }
231
232 pub fn load_tasks_manager_from<P: AsRef<Path>, D: AsRef<Path>>(
233 &self,
234 workspace_root: P,
235 config_dir: D,
236 ) -> miette::Result<InheritedTasksManager> {
237 let workspace_root = workspace_root.as_ref();
238 let config_dir = config_dir.as_ref();
239 let mut manager = InheritedTasksManager::default();
240
241 for file in self.get_tasks_files(config_dir)? {
243 if file.exists() {
244 manager.add_config(
245 workspace_root,
246 &file,
247 self.load_tasks_config_from_path(workspace_root, &file)?,
248 )?;
249 }
250 }
251
252 Ok(manager)
253 }
254
255 pub fn load_template_config<P: AsRef<Path>>(
256 &self,
257 template_root: P,
258 ) -> miette::Result<TemplateConfig> {
259 let result = self.create_template_loader(template_root)?.load()?;
260
261 Ok(result.config)
262 }
263
264 pub fn load_toolchains_config<P: AsRef<Path>>(
265 &self,
266 workspace_root: P,
267 #[cfg(feature = "proto")] proto_config: &proto_core::ProtoConfig,
268 ) -> miette::Result<ToolchainsConfig> {
269 let mut result = self.create_toolchains_loader(workspace_root)?.load()?;
270 result.config.inherit_versions_from_env_vars()?;
271
272 #[cfg(feature = "proto")]
273 {
274 use proto_core::PluginLocator;
275
276 result.config.inherit_defaults(proto_config)?;
277
278 for config in result.config.plugins.values_mut() {
280 if let Some(PluginLocator::File(file)) = &mut config.plugin {
281 let file_path = file.get_unresolved_path();
282
283 file.path = Some(if file_path.is_absolute() {
284 file_path
285 } else {
286 self.dir.join(file_path)
287 });
288 }
289 }
290 }
291
292 Ok(result.config)
293 }
294
295 pub fn load_workspace_config<P: AsRef<Path>>(
296 &self,
297 workspace_root: P,
298 ) -> miette::Result<WorkspaceConfig> {
299 let result = self.create_workspace_loader(workspace_root)?.load()?;
300
301 Ok(result.config)
302 }
303
304 pub fn prepare_loader<T: Config>(
305 &self,
306 loader: &mut Loader<T>,
307 files: Vec<PathBuf>,
308 ) -> miette::Result<()> {
309 loader.add_format(HclFormat::default());
310
311 for file in files {
312 loader.file_optional(file)?;
313 }
314
315 Ok(())
316 }
317
318 pub fn get_debug_label(&self, name: &str) -> String {
319 self.finder.get_debug_label(name)
320 }
321
322 pub fn get_debug_label_root(&self, name: &str) -> String {
323 self.finder.get_debug_label_root(name, &self.dir)
324 }
325
326 pub fn get_extensions_files(&self) -> Vec<PathBuf> {
327 self.finder
328 .get_extensions_file_names()
329 .into_iter()
330 .map(|name| self.dir.join(name))
331 .collect()
332 }
333
334 pub fn get_project_files(&self, project_root: &Path) -> Vec<PathBuf> {
335 self.finder
336 .get_project_file_names()
337 .into_iter()
338 .map(|name| project_root.join(name))
339 .collect()
340 }
341
342 pub fn get_tasks_files(&self, tasks_dir: &Path) -> miette::Result<Vec<PathBuf>> {
343 self.finder.get_from_dir(tasks_dir.join("tasks"))
344 }
345
346 pub fn get_template_files(&self, template_root: &Path) -> Vec<PathBuf> {
347 self.finder
348 .get_template_file_names()
349 .into_iter()
350 .map(|name| template_root.join(name))
351 .collect()
352 }
353
354 pub fn get_toolchains_files(&self) -> Vec<PathBuf> {
355 self.finder
356 .get_toolchains_file_names()
357 .into_iter()
358 .map(|name| self.dir.join(name))
359 .collect()
360 }
361
362 pub fn get_workspace_files(&self) -> Vec<PathBuf> {
363 self.finder
364 .get_workspace_file_names()
365 .into_iter()
366 .map(|name| self.dir.join(name))
367 .collect()
368 }
369}
370
371impl Deref for ConfigLoader {
372 type Target = ConfigFinder;
373
374 fn deref(&self) -> &Self::Target {
375 &self.finder
376 }
377}