Преглед изворни кода

config errors name every searched path

- gconfig::read_config: a missing ./config.toml+global combo no longer
  surfaces as a bare "No such file or directory (os error 2)" — the error
  now lists both searched locations and hints at --config / init-global;
  an unreadable explicit --config path is named too.
- `sm121 config path|init-global` dispatch before cluster_sources
  merging, so the diagnostic command itself cannot be broken by a stale
  source pointer (it was exactly how the moved-launcher global config
  became undiagnosable).
kilork пре 1 месец
родитељ
комит
ee2855da38
2 измењених фајлова са 54 додато и 6 уклоњено
  1. 47 0
      src/gconfig.rs
  2. 7 6
      src/main.rs

+ 47 - 0
src/gconfig.rs

@@ -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!(

+ 7 - 6
src/main.rs

@@ -57,11 +57,15 @@ async fn main() -> Result<()> {
         .init();
     let cli = Cli::parse();
 
-    let config_path = crate::gconfig::resolve_config_path(&cli.config);
-    let raw_config = tokio::fs::read_to_string(&config_path).await?;
+    let (config_path, raw_config) = crate::gconfig::read_config(&cli.config).await?;
     let mut config: Config = toml::from_str(&raw_config)?;
     crate::config::merge_cluster_sources(&mut config).await?;
     let dry_run = cli.dry_run;
+    // `config` subcommands are pure path/file utilities: dispatch them
+    // before cluster_sources merging so they work even with broken sources.
+    if let Commands::Config(cmd) = &cli.command {
+        return crate::gconfig::run(cmd, &cli.config, dry_run);
+    }
 
     match cli.command {
         Commands::Serve {
@@ -176,10 +180,7 @@ async fn main() -> Result<()> {
             )
             .await?;
         }
-        Commands::Config(cmd) => match cmd {
-            crate::cli::ConfigCommands::Path => crate::gconfig::show_paths(&cli.config),
-            crate::cli::ConfigCommands::InitGlobal => crate::gconfig::init_global(dry_run)?,
-        },
+        Commands::Config(_) => unreachable!("dispatched above, before merging"),
         Commands::Web {
             port,
             bind,