1use std::path::{Path, PathBuf};
7
8use ito_common::fs::FileSystem;
9
10use crate::{ConfigContext, ResolvedConfig, ito_config_dir, load_cascading_project_config_fs};
11
12#[derive(Debug, Clone)]
13pub struct ItoContext {
15 pub config_dir: Option<PathBuf>,
17
18 pub project_root: PathBuf,
20
21 pub ito_path: Option<PathBuf>,
23
24 pub config: ResolvedConfig,
26}
27
28impl ItoContext {
29 pub fn resolve<F: FileSystem>(fs: &F, project_root: &Path) -> Self {
31 let ctx = ConfigContext::from_process_env();
32 Self::resolve_with_ctx(fs, project_root, ctx)
33 }
34
35 pub fn resolve_with_ctx<F: FileSystem>(
37 fs: &F,
38 project_root: &Path,
39 ctx: ConfigContext,
40 ) -> Self {
41 let project_root = project_root.to_path_buf();
42 let ito_path = crate::ito_dir::get_ito_path_fs(fs, &project_root, &ctx);
43 let config_dir = ito_config_dir(&ctx);
44
45 let config = load_cascading_project_config_fs(fs, &project_root, &ito_path, &ctx);
46
47 let ito_path = fs.is_dir(&ito_path).then_some(ito_path);
48
49 Self {
50 config_dir,
51 project_root,
52 ito_path,
53 config,
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 use ito_common::fs::StdFs;
63
64 #[test]
65 fn resolve_with_ctx_sets_none_when_ito_dir_is_missing() {
66 let project = tempfile::tempdir().expect("tempdir");
67 let ctx = ConfigContext::default();
68
69 let resolved = ItoContext::resolve_with_ctx(&StdFs, project.path(), ctx);
70
71 assert_eq!(resolved.project_root, project.path());
72 assert_eq!(resolved.ito_path, None);
73 assert_eq!(resolved.config.loaded_from, Vec::<PathBuf>::new());
74 }
75
76 #[test]
77 fn resolve_with_ctx_sets_ito_path_when_directory_exists() {
78 let project = tempfile::tempdir().expect("tempdir");
79 let ito_dir = project.path().join(".ito");
80 std::fs::create_dir_all(&ito_dir).expect("create .ito dir");
81
82 let resolved =
83 ItoContext::resolve_with_ctx(&StdFs, project.path(), ConfigContext::default());
84
85 assert_eq!(resolved.ito_path, Some(ito_dir));
86 }
87
88 #[test]
89 fn resolve_with_ctx_uses_explicit_config_context_paths() {
90 let project = tempfile::tempdir().expect("tempdir");
91 let xdg_home = project.path().join("xdg");
92 let ctx = ConfigContext {
93 xdg_config_home: Some(xdg_home.clone()),
94 home_dir: Some(project.path().join("home")),
95 project_dir: None,
96 };
97
98 let resolved = ItoContext::resolve_with_ctx(&StdFs, project.path(), ctx);
99
100 assert_eq!(resolved.config_dir, Some(xdg_home.join("ito")));
101 }
102}