Skip to main content

runex_core/
config.rs

1use std::path::PathBuf;
2
3use crate::model::Config;
4
5#[derive(Debug, thiserror::Error)]
6pub enum ConfigError {
7    #[error("TOML parse error: {0}")]
8    Parse(#[from] toml::de::Error),
9    #[error("IO error: {0}")]
10    Io(#[from] std::io::Error),
11    #[error("cannot determine config directory")]
12    NoConfigDir,
13}
14
15/// Parse a TOML string into Config.
16pub fn parse_config(s: &str) -> Result<Config, ConfigError> {
17    let config: Config = toml::from_str(s)?;
18    Ok(config)
19}
20
21/// Default config file path: `~/.config/runex/config.toml` (or platform equivalent).
22/// Overridden by `RUNEX_CONFIG` env var.
23pub fn default_config_path() -> Result<PathBuf, ConfigError> {
24    if let Ok(p) = std::env::var("RUNEX_CONFIG") {
25        return Ok(PathBuf::from(p));
26    }
27    let dir = dirs::config_dir().ok_or(ConfigError::NoConfigDir)?;
28    Ok(dir.join("runex").join("config.toml"))
29}
30
31/// Load config from a file path.
32pub fn load_config(path: &std::path::Path) -> Result<Config, ConfigError> {
33    let content = std::fs::read_to_string(path)?;
34    parse_config(&content)
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn parse_minimal_toml() {
43        let toml = r#"
44version = 1
45
46[[abbr]]
47key = "gcm"
48expand = "git commit -m"
49"#;
50        let config = parse_config(toml).unwrap();
51        assert_eq!(config.version, 1);
52        assert_eq!(config.abbr.len(), 1);
53        assert_eq!(config.abbr[0].key, "gcm");
54        assert_eq!(config.abbr[0].expand, "git commit -m");
55    }
56
57    #[test]
58    fn parse_with_when_command_exists() {
59        let toml = r#"
60version = 1
61
62[[abbr]]
63key = "ls"
64expand = "lsd"
65when_command_exists = ["lsd"]
66"#;
67        let config = parse_config(toml).unwrap();
68        assert_eq!(
69            config.abbr[0].when_command_exists,
70            Some(vec!["lsd".to_string()])
71        );
72    }
73
74    #[test]
75    fn parse_missing_version_is_err() {
76        let toml = r#"
77[[abbr]]
78key = "gcm"
79expand = "git commit -m"
80"#;
81        assert!(parse_config(toml).is_err());
82    }
83
84    #[test]
85    fn parse_empty_abbr_list() {
86        let toml = "version = 1\n";
87        let config = parse_config(toml).unwrap();
88        assert!(config.abbr.is_empty());
89    }
90
91    #[test]
92    fn load_config_from_file() {
93        let dir = std::env::temp_dir().join("runex_test_load");
94        std::fs::create_dir_all(&dir).unwrap();
95        let path = dir.join("config.toml");
96        std::fs::write(
97            &path,
98            r#"
99version = 1
100
101[[abbr]]
102key = "gcm"
103expand = "git commit -m"
104"#,
105        )
106        .unwrap();
107
108        let config = load_config(&path).unwrap();
109        assert_eq!(config.version, 1);
110        assert_eq!(config.abbr[0].key, "gcm");
111
112        std::fs::remove_dir_all(&dir).ok();
113    }
114
115    #[test]
116    fn default_config_path_env_override() {
117        std::env::set_var("RUNEX_CONFIG", "/tmp/custom.toml");
118        let path = default_config_path().unwrap();
119        assert_eq!(path, PathBuf::from("/tmp/custom.toml"));
120        std::env::remove_var("RUNEX_CONFIG");
121    }
122}