Skip to main content

auths_cli/commands/
config.rs

1//! `auths config` command — view and modify `~/.auths/config.toml`.
2
3use crate::commands::executable::ExecutableCommand;
4use crate::config::CliConfig;
5use anyhow::{Result, bail};
6use auths_core::config::{AuthsConfig, PassphraseCachePolicy, load_config, save_config};
7use clap::{Parser, Subcommand};
8
9/// Manage Auths configuration.
10#[derive(Parser, Debug, Clone)]
11#[command(name = "config", about = "View and modify Auths configuration")]
12pub struct ConfigCommand {
13    #[command(subcommand)]
14    pub action: ConfigAction,
15}
16
17/// Config subcommands.
18#[derive(Subcommand, Debug, Clone)]
19pub enum ConfigAction {
20    /// Set a configuration value (e.g. `auths config set passphrase.cache always`).
21    Set {
22        /// Dotted key path (e.g. `passphrase.cache`, `passphrase.duration`).
23        key: String,
24        /// Value to assign.
25        value: String,
26    },
27    /// Get a configuration value (e.g. `auths config get passphrase.cache`).
28    Get {
29        /// Dotted key path.
30        key: String,
31    },
32    /// Show the full configuration.
33    Show,
34}
35
36impl ExecutableCommand for ConfigCommand {
37    fn execute(&self, _ctx: &CliConfig) -> Result<()> {
38        match &self.action {
39            ConfigAction::Set { key, value } => execute_set(key, value),
40            ConfigAction::Get { key } => execute_get(key),
41            ConfigAction::Show => execute_show(),
42        }
43    }
44}
45
46fn execute_set(key: &str, value: &str) -> Result<()> {
47    let mut config = load_config();
48
49    match key {
50        "passphrase.cache" => {
51            config.passphrase.cache = parse_cache_policy(value)?;
52        }
53        "passphrase.duration" => {
54            auths_core::storage::passphrase_cache::parse_duration_str(value).ok_or_else(|| {
55                anyhow::anyhow!(
56                    "Invalid duration '{}'. Use formats like '7d', '24h', '30m', '3600s'.",
57                    value
58                )
59            })?;
60            config.passphrase.duration = Some(value.to_string());
61        }
62        "passphrase.biometric" => {
63            config.passphrase.biometric = parse_bool(value)?;
64        }
65        _ => bail!(
66            "Unknown config key '{}'. Valid keys: passphrase.cache, passphrase.duration, passphrase.biometric",
67            key
68        ),
69    }
70
71    save_config(&config)?;
72    println!("Set {} = {}", key, value);
73    Ok(())
74}
75
76fn execute_get(key: &str) -> Result<()> {
77    let config = load_config();
78
79    match key {
80        "passphrase.cache" => {
81            let label = policy_label(&config.passphrase.cache);
82            println!("{}", label);
83        }
84        "passphrase.duration" => {
85            println!(
86                "{}",
87                config.passphrase.duration.as_deref().unwrap_or("(not set)")
88            );
89        }
90        "passphrase.biometric" => {
91            println!("{}", config.passphrase.biometric);
92        }
93        _ => bail!(
94            "Unknown config key '{}'. Valid keys: passphrase.cache, passphrase.duration, passphrase.biometric",
95            key
96        ),
97    }
98
99    Ok(())
100}
101
102fn execute_show() -> Result<()> {
103    let config = load_config();
104    let toml_str = toml::to_string_pretty(&config)
105        .map_err(|e| anyhow::anyhow!("Failed to serialize config: {}", e))?;
106    println!("{}", toml_str);
107    Ok(())
108}
109
110fn parse_cache_policy(s: &str) -> Result<PassphraseCachePolicy> {
111    match s.to_lowercase().as_str() {
112        "always" => Ok(PassphraseCachePolicy::Always),
113        "session" => Ok(PassphraseCachePolicy::Session),
114        "duration" => Ok(PassphraseCachePolicy::Duration),
115        "never" => Ok(PassphraseCachePolicy::Never),
116        _ => bail!(
117            "Invalid cache policy '{}'. Valid values: always, session, duration, never",
118            s
119        ),
120    }
121}
122
123fn policy_label(policy: &PassphraseCachePolicy) -> &'static str {
124    match policy {
125        PassphraseCachePolicy::Always => "always",
126        PassphraseCachePolicy::Session => "session",
127        PassphraseCachePolicy::Duration => "duration",
128        PassphraseCachePolicy::Never => "never",
129    }
130}
131
132fn parse_bool(s: &str) -> Result<bool> {
133    match s.to_lowercase().as_str() {
134        "true" | "1" | "yes" => Ok(true),
135        "false" | "0" | "no" => Ok(false),
136        _ => bail!("Invalid boolean '{}'. Use true/false, yes/no, or 1/0", s),
137    }
138}
139
140fn _ensure_default_config_exists() -> Result<AuthsConfig> {
141    let config = load_config();
142    save_config(&config)?;
143    Ok(config)
144}