| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- //! Global config management: path resolution, `config path` display,
- //! `config init-global` scaffold.
- use crate::config::default_config_dir;
- use std::path::PathBuf;
- /// Global config template written by `config init-global`.
- const GLOBAL_TEMPLATE: &str = r#"# Global sm121 config (created by `sm121 config init-global`).
- # Used when ./config.toml is absent, so `sm121` works from any cwd.
- # `ssh_user` / `ssh_key_path` may instead be provided by a cluster source.
- # `~` is expanded.
- #
- # recipe_sources = ["/path/to/recipes"] # extra recipe dirs
- # cluster_sources = ["/path/to/clusters.toml"] # extra cluster def files
- "#;
- /// Resolve the path to the config file.
- ///
- /// Search order:
- /// 1. `--config <path>` from CLI (always wins; error if missing)
- /// 2. `./config.toml` in cwd
- /// 3. `<default_config_dir>/config.toml`
- ///
- /// Returns the explicit path if nothing is found; the loader reports a
- /// clean error.
- pub fn resolve_config_path(cli_config: &str) -> PathBuf {
- let explicit = PathBuf::from(cli_config);
- // User passed a non-default path: use it as-is.
- if cli_config != "config.toml" {
- return explicit;
- }
- // Default value (or no flag): try cwd first, then default folder.
- if explicit.exists() {
- return explicit;
- }
- if let Some(dir) = default_config_dir() {
- let candidate = dir.join("config.toml");
- if candidate.exists() {
- return candidate;
- }
- }
- explicit
- }
- /// `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).
- pub fn show_paths(cli_config: &str) {
- let active = resolve_config_path(cli_config);
- let origin = if cli_config != "config.toml" {
- "--config"
- } else if std::path::Path::new("config.toml").exists() {
- "./config.toml (cwd)"
- } else {
- "global config dir"
- };
- println!("active config: {} [{}]", active.display(), origin);
- if let Some(dir) = default_config_dir() {
- let global = dir.join("config.toml");
- let state = if global.exists() {
- "exists"
- } else {
- "missing — `sm121 config init-global` creates a template"
- };
- println!("global config: {} [{}]", global.display(), state);
- }
- }
- /// `sm121 config init-global`: create the global config (template) if
- /// missing. Never overwrites an existing file.
- pub fn init_global(dry_run: bool) -> anyhow::Result<()> {
- let dir = default_config_dir().ok_or_else(|| {
- anyhow::anyhow!("cannot determine the platform config dir (dirs::config_dir)")
- })?;
- let global = dir.join("config.toml");
- if global.exists() {
- println!("global config already exists: {}", global.display());
- return Ok(());
- }
- if dry_run {
- println!("would write: {}", global.display());
- return Ok(());
- }
- std::fs::create_dir_all(&dir)?;
- std::fs::write(&global, GLOBAL_TEMPLATE)?;
- println!("created global config template: {}", global.display());
- Ok(())
- }
- #[cfg(test)]
- mod tests {
- use super::resolve_config_path;
- #[test]
- fn explicit_config_path_wins() {
- assert_eq!(
- resolve_config_path("/tmp/explicit-sm121.toml"),
- std::path::PathBuf::from("/tmp/explicit-sm121.toml")
- );
- }
- #[test]
- fn default_config_dir_is_some() {
- assert!(crate::config::default_config_dir().is_some());
- }
- }
|