auths_cli/commands/
config.rs1use 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#[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#[derive(Subcommand, Debug, Clone)]
19pub enum ConfigAction {
20 Set {
22 key: String,
24 value: String,
26 },
27 Get {
29 key: String,
31 },
32 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}