Skip to main content

moon_config/
config_loader.rs

1use 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, // .moon
19    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        // Do not prepare since there are many tasks paths!
92
93        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            // Resolve plugin file locations
161            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        // tasks/**/*.*
242        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            // Resolve plugin file locations
279            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}