Skip to main content

vs_config/
config_file.rs

1//! Reading and writing persisted `vs` configuration files.
2
3use std::fs;
4use std::path::{Path, PathBuf};
5
6use serde_yaml::Value;
7
8use crate::ConfigError;
9use crate::types::{AppConfig, ToolVersions};
10
11/// Reads `config.yaml` from the active home.
12pub fn read_app_config(home: &Path) -> Result<AppConfig, ConfigError> {
13    let path = home.join("config.yaml");
14    if !path.exists() {
15        return Ok(AppConfig::default());
16    }
17
18    let content = fs::read_to_string(&path)?;
19    serde_yaml::from_str(&content).map_err(|error| ConfigError::Yaml {
20        path,
21        message: error.to_string(),
22    })
23}
24
25/// Writes `config.yaml` to the active home.
26pub fn write_app_config(home: &Path, config: &AppConfig) -> Result<(), ConfigError> {
27    fs::create_dir_all(home)?;
28    let path = home.join("config.yaml");
29    let rendered = serde_yaml::to_string(config).map_err(|error| ConfigError::Yaml {
30        path: path.clone(),
31        message: error.to_string(),
32    })?;
33    fs::write(path, rendered)?;
34    Ok(())
35}
36
37/// Reads a TOML tool file.
38pub fn read_tool_versions(path: &Path) -> Result<ToolVersions, ConfigError> {
39    let content = fs::read_to_string(path)?;
40    toml::from_str(&content).map_err(|error| ConfigError::Toml {
41        path: path.to_path_buf(),
42        message: error.to_string(),
43    })
44}
45
46/// Writes a TOML tool file.
47pub fn write_tool_versions(path: &Path, tools: &ToolVersions) -> Result<(), ConfigError> {
48    if let Some(parent) = path.parent() {
49        fs::create_dir_all(parent)?;
50    }
51    let rendered = toml::to_string_pretty(tools).map_err(|error| ConfigError::Toml {
52        path: path.to_path_buf(),
53        message: error.to_string(),
54    })?;
55    fs::write(path, rendered)?;
56    Ok(())
57}
58
59/// Lists user-visible configuration values as strings.
60pub fn flatten_app_config(config: &AppConfig) -> Vec<(String, String)> {
61    vec![
62        (
63            String::from("proxy.enable"),
64            config.proxy.enable.to_string(),
65        ),
66        (
67            String::from("proxy.url"),
68            if config.proxy.url.is_empty() {
69                String::from("<unset>")
70            } else {
71                config.proxy.url.clone()
72            },
73        ),
74        (
75            String::from("storage.sdkPath"),
76            if config.storage.sdk_path.is_empty() {
77                String::from("<unset>")
78            } else {
79                config.storage.sdk_path.clone()
80            },
81        ),
82        (
83            String::from("registry.address"),
84            if config.registry.address.is_empty() {
85                String::from("<unset>")
86            } else {
87                config.registry.address.clone()
88            },
89        ),
90        (
91            String::from("legacyVersionFile.enable"),
92            config.legacy_version_file.enable.to_string(),
93        ),
94        (
95            String::from("legacyVersionFile.strategy"),
96            config.legacy_version_file.strategy.clone(),
97        ),
98        (
99            String::from("cache.availableHookDuration"),
100            config.cache.available_hook_duration.clone(),
101        ),
102    ]
103}
104
105/// Sets a top-level config value by key.
106pub fn set_app_config_value(
107    config: &mut AppConfig,
108    key: &str,
109    value: &str,
110) -> Result<(), ConfigError> {
111    match key {
112        "proxy.enable" => {
113            config.proxy.enable = value
114                .parse::<bool>()
115                .map_err(|_| ConfigError::InvalidValue {
116                    key: key.to_string(),
117                    value: value.to_string(),
118                })?;
119        }
120        "proxy.url" => {
121            config.proxy.url = value.to_string();
122        }
123        "storage.sdkPath" => {
124            config.storage.sdk_path = value.to_string();
125        }
126        "registry.address" => {
127            config.registry.address = value.to_string();
128        }
129        "legacyVersionFile.enable" => {
130            config.legacy_version_file.enable =
131                value
132                    .parse::<bool>()
133                    .map_err(|_| ConfigError::InvalidValue {
134                        key: key.to_string(),
135                        value: value.to_string(),
136                    })?;
137        }
138        "legacyVersionFile.strategy" => {
139            config.legacy_version_file.strategy = value.to_string();
140        }
141        "cache.availableHookDuration" => {
142            config.cache.available_hook_duration = value.to_string();
143        }
144        _ => {
145            return Err(ConfigError::UnknownKey(key.to_string()));
146        }
147    }
148    Ok(())
149}
150
151/// Unsets a top-level config value by key.
152pub fn unset_app_config_value(config: &mut AppConfig, key: &str) -> Result<(), ConfigError> {
153    match key {
154        "proxy.enable" => {
155            config.proxy.enable = AppConfig::default().proxy.enable;
156        }
157        "proxy.url" => {
158            config.proxy.url.clear();
159        }
160        "storage.sdkPath" => {
161            config.storage.sdk_path.clear();
162        }
163        "registry.address" => {
164            config.registry.address.clear();
165        }
166        "legacyVersionFile.enable" => {
167            config.legacy_version_file.enable = AppConfig::default().legacy_version_file.enable;
168        }
169        "legacyVersionFile.strategy" => {
170            config.legacy_version_file.strategy = AppConfig::default().legacy_version_file.strategy;
171        }
172        "cache.availableHookDuration" => {
173            config.cache.available_hook_duration =
174                AppConfig::default().cache.available_hook_duration;
175        }
176        _ => {
177            return Err(ConfigError::UnknownKey(key.to_string()));
178        }
179    }
180    Ok(())
181}
182
183/// Converts an application config to a YAML value for debugging.
184pub fn app_config_to_value(config: &AppConfig) -> Result<Value, ConfigError> {
185    serde_yaml::to_value(config).map_err(|error| ConfigError::Yaml {
186        path: PathBuf::from("config.yaml"),
187        message: error.to_string(),
188    })
189}