|
|
@@ -42,6 +42,44 @@ pub fn resolve_config_path(cli_config: &str) -> PathBuf {
|
|
|
explicit
|
|
|
}
|
|
|
|
|
|
+/// Resolve + read the config in one step. A missing file must not surface
|
|
|
+/// as a bare "No such file or directory (os error 2)" — the error names
|
|
|
+/// every location that was searched.
|
|
|
+pub async fn read_config(cli_config: &str) -> anyhow::Result<(PathBuf, String)> {
|
|
|
+ let path = resolve_config_path(cli_config);
|
|
|
+ match tokio::fs::read_to_string(&path).await {
|
|
|
+ Ok(raw) => Ok((path, raw)),
|
|
|
+ Err(e) => {
|
|
|
+ let msg = if path == std::path::Path::new("config.toml") {
|
|
|
+ // resolve_config_path's "found nowhere" sentinel
|
|
|
+ let mut m = String::from("config not found; searched: ./config.toml");
|
|
|
+ if let Some(dir) = default_config_dir() {
|
|
|
+ m.push_str(&format!(", {}/config.toml", dir.display()));
|
|
|
+ }
|
|
|
+ m.push_str(" — pass --config <path> or run `sm121 config init-global`");
|
|
|
+ m
|
|
|
+ } else {
|
|
|
+ format!("cannot read config {}", path.display())
|
|
|
+ };
|
|
|
+ anyhow::bail!("{msg} ({e})")
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Entry point for `sm121 config ...` subcommands (dispatched before
|
|
|
+/// cluster_sources merging so they work even with broken sources).
|
|
|
+pub fn run(
|
|
|
+ cmd: &crate::cli::ConfigCommands,
|
|
|
+ cli_config: &str,
|
|
|
+ dry_run: bool,
|
|
|
+) -> anyhow::Result<()> {
|
|
|
+ match cmd {
|
|
|
+ crate::cli::ConfigCommands::Path => show_paths(cli_config),
|
|
|
+ crate::cli::ConfigCommands::InitGlobal => init_global(dry_run)?,
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
/// `sm121 config path`: print the active config and the global config
|
|
|
/// location (the part that is not transparent — it lives in the
|
|
|
/// platform config dir, e.g. `~/Library/Application Support/sm121` on macOS).
|
|
|
@@ -91,6 +129,15 @@ pub fn init_global(dry_run: bool) -> anyhow::Result<()> {
|
|
|
mod tests {
|
|
|
use super::resolve_config_path;
|
|
|
|
|
|
+ #[tokio::test]
|
|
|
+ async fn missing_config_error_names_the_paths_searched() {
|
|
|
+ let err = super::read_config("/nonexistent/sm121-config.toml")
|
|
|
+ .await
|
|
|
+ .unwrap_err()
|
|
|
+ .to_string();
|
|
|
+ assert!(err.contains("/nonexistent/sm121-config.toml"), "{err}");
|
|
|
+ }
|
|
|
+
|
|
|
#[test]
|
|
|
fn explicit_config_path_wins() {
|
|
|
assert_eq!(
|