gather_all_code_from_crates/
global_config.rs

1crate::ix!();
2
3#[derive(Debug, Clone, Deserialize, Builder, Getters, Setters)]
4#[builder(setter(into))]
5#[getset(get = "pub", set = "pub")]
6pub struct GlobalConfig {
7    #[serde(default)]
8    #[builder(default)]
9    project_overrides: HashMap<String, AstFilterCriteria>,
10
11    #[serde(default)]
12    #[builder(default)]
13    default_include_tests: bool,
14
15    #[serde(default)]
16    #[builder(default)]
17    default_omit_bodies: bool,
18
19    #[serde(default)]
20    #[builder(default)]
21    extra_flags: u64,
22}
23
24impl GlobalConfig {
25    fn load_from_file(path: &PathBuf) -> AppResult<GlobalConfig> {
26        if !path.exists() {
27            return Ok(GlobalConfigBuilder::default().build().map_err(|_|AppError::Config{reason:ErrorReason::Config})?);
28        }
29        let file = File::open(path).map_err(|e|AppError::Io{code:e.kind()})?;
30        let mut reader = BufReader::new(file);
31        let mut content = String::new();
32        reader.read_to_string(&mut content).map_err(|e|AppError::Io{code:e.kind()})?;
33        let cfg: GlobalConfig = serde_json::from_str(&content).map_err(|_|
34            AppError::Config{reason:ErrorReason::Parse}
35        )?;
36        Ok(cfg)
37    }
38}
39
40pub fn load_global_config() -> AppResult<GlobalConfig> {
41    let home = std::env::var("HOME").map_err(|_|AppError::Config{reason:ErrorReason::MissingData})?;
42    let config_path = PathBuf::from(home).join(".gather-all-code-from-crates");
43    GlobalConfig::load_from_file(&config_path)
44}
45
46#[cfg(test)]
47mod global_config_tests {
48    use super::*;
49
50    #[test]
51    fn test_effective_config_from_cli_and_global() {
52        let global_cfg = GlobalConfigBuilder::default()
53            .default_include_tests(true)
54            .default_omit_bodies(false)
55            .build().unwrap();
56
57        let cli = CliBuilder::default()
58            .crates(vec![])
59            .include_tests(false)
60            .single_test_name(None)
61            .omit_private(true)
62            .omit_bodies(false)
63            .single_function_name(None)
64            .excluded_files(vec![])
65            .exclude_main_file(false)
66            .remove_doc_comments(false)
67            .build()
68            .unwrap();
69
70
71        let eff = EffectiveConfig::from_cli_and_global(&cli, &global_cfg).unwrap();
72        // include_tests should be true from global default OR cli (cli=false, global=true => result = true)
73        assert_eq!(*eff.criteria().include_tests(), true);
74        assert_eq!(*eff.criteria().omit_private(), true); // from cli
75        assert_eq!(*eff.criteria().omit_bodies(), false); // global false, cli false
76    }
77
78    #[test]
79    fn test_global_config_load_defaults_if_missing() {
80        let path = PathBuf::from("non_existent_config_file.json");
81        let cfg = GlobalConfig::load_from_file(&path).unwrap();
82        assert_eq!(cfg.project_overrides().len(), 0);
83        assert_eq!(*cfg.default_include_tests(), false);
84        assert_eq!(*cfg.default_omit_bodies(), false);
85    }
86
87    #[test]
88    fn test_global_config_load_from_file() {
89        let tmp_dir = TempDir::new().unwrap();
90        let cfg_path = tmp_dir.path().join("config.json");
91        let content = r#"
92        {
93          "project_overrides": {
94            "mycrate": {
95              "include_tests": true,
96              "omit_private": true
97            }
98          },
99          "default_include_tests": true,
100          "default_omit_bodies": false,
101          "extra_flags": 42
102        }
103        "#;
104
105        {
106            let mut f = File::create(&cfg_path).unwrap();
107            f.write_all(content.as_bytes()).unwrap();
108        }
109
110        let cfg = GlobalConfig::load_from_file(&cfg_path).unwrap();
111        assert_eq!(cfg.project_overrides().len(), 1);
112        assert!(cfg.project_overrides().get("mycrate").unwrap().include_tests());
113        assert!(cfg.project_overrides().get("mycrate").unwrap().omit_private());
114        assert_eq!(*cfg.default_include_tests(), true);
115        assert_eq!(*cfg.default_omit_bodies(), false);
116        assert_eq!(*cfg.extra_flags(), 42);
117    }
118
119    #[test]
120    fn test_global_config_load_failure_on_invalid_json() {
121        let tmp_dir = TempDir::new().unwrap();
122        let cfg_path = tmp_dir.path().join("config.json");
123        {
124            let mut f = File::create(&cfg_path).unwrap();
125            f.write_all(b"{ invalid json }").unwrap();
126        }
127
128        let result = GlobalConfig::load_from_file(&cfg_path);
129        match result {
130            Err(AppError::Config { reason: ErrorReason::Parse }) => (),
131            _ => panic!("Expected parse error"),
132        }
133    }
134
135    #[test]
136    fn test_global_config_missing_home_env() {
137        // Temporarily unset HOME and ensure error is returned
138        let original_home = std::env::var_os("HOME");
139        std::env::remove_var("HOME");
140
141        let result = load_global_config();
142        match result {
143            Err(AppError::Config{reason:ErrorReason::MissingData}) => (),
144            _ => panic!("Expected MissingData error when HOME is unset"),
145        }
146
147        // Restore HOME
148        if let Some(h) = original_home {
149            std::env::set_var("HOME", h);
150        }
151    }
152}