use anyhow::Result;
use crate::cli::KeysCommands;
use crate::config::{Config, KeyManager};
use crate::error::CliError;
use crate::output;
const REMOVE_DEFAULT_ERROR: &str = concat!(
"Cannot remove the 'default' key because the CLI requires it. ",
"Use --force to remove it anyway, or replace it with ",
"'thru keys add --overwrite default <key>'."
);
pub async fn handle_keys_command(
config: &Config,
subcommand: KeysCommands,
json_format: bool,
) -> Result<(), CliError> {
match subcommand {
KeysCommands::List => list_keys(config, json_format).await,
KeysCommands::Add {
overwrite,
name,
key,
} => add_key(config, &name, &key, overwrite, json_format).await,
KeysCommands::Get { name } => get_key(config, &name, json_format).await,
KeysCommands::Generate { overwrite, name } => {
generate_key(config, &name, overwrite, json_format).await
}
KeysCommands::Remove { force, name } => {
remove_key(config, &name, force, json_format).await
}
}
}
async fn list_keys(config: &Config, json_format: bool) -> Result<(), CliError> {
let key_names = config.keys.list_keys();
let response = output::create_keys_list_response(key_names);
output::print_output(response, json_format);
Ok(())
}
async fn add_key(
_config: &Config,
name: &str,
key: &str,
overwrite: bool,
json_format: bool,
) -> Result<(), CliError> {
let mut current_config = Config::load_for_key_management().await?;
current_config.keys.add_key(name, key, overwrite)?;
save_config(¤t_config).await?;
let response = output::create_keys_operation_response("add", name, "success", None);
output::print_output(response, json_format);
Ok(())
}
async fn get_key(config: &Config, name: &str, json_format: bool) -> Result<(), CliError> {
let key_value = config.keys.get_key(name)?;
let response = output::create_keys_operation_response("get", name, "success", Some(key_value));
output::print_output(response, json_format);
Ok(())
}
async fn generate_key(
_config: &Config,
name: &str,
overwrite: bool,
json_format: bool,
) -> Result<(), CliError> {
let mut current_config = Config::load_for_key_management().await?;
let generated_key = current_config.keys.generate_key(name, overwrite)?;
save_config(¤t_config).await?;
let response =
output::create_keys_operation_response("generate", name, "success", Some(&generated_key));
output::print_output(response, json_format);
Ok(())
}
async fn remove_key(
_config: &Config,
name: &str,
force: bool,
json_format: bool,
) -> Result<(), CliError> {
let mut current_config = Config::load_for_key_management().await?;
let removed_default = remove_key_from_config(&mut current_config, name, force)?;
save_config(¤t_config).await?;
if removed_default && !json_format {
output::print_warning(
"Removed 'default' key. Commands that omit a key or fee payer will fail until you recreate it with 'thru keys generate default' or 'thru keys add --overwrite default <key>'.",
);
}
let response = output::create_keys_operation_response("remove", name, "success", None);
output::print_output(response, json_format);
Ok(())
}
fn remove_key_from_config(config: &mut Config, name: &str, force: bool) -> Result<bool, CliError> {
let removing_default = KeyManager::is_default_key_name(name);
if removing_default && !force {
return Err(CliError::Validation(REMOVE_DEFAULT_ERROR.to_string()));
}
config.keys.remove_key(name)?;
Ok(removing_default)
}
async fn save_config(config: &Config) -> Result<(), CliError> {
let config_path = Config::get_config_path()?;
let config_content = serde_yaml::to_string(config).map_err(|e| CliError::Generic {
message: format!("Failed to serialize config: {}", e),
})?;
tokio::fs::write(&config_path, config_content)
.await
.map_err(|e| CliError::Generic {
message: format!("Failed to write config file: {}", e),
})?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_list_keys() {
let config = Config::default();
let result = list_keys(&config, true).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_get_key() {
let config = Config::default();
let result = get_key(&config, "default", true).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_get_nonexistent_key() {
let config = Config::default();
let result = get_key(&config, "nonexistent", true).await;
assert!(result.is_err());
}
#[test]
fn test_remove_default_without_force_is_validation_error() {
let mut config = Config::default();
let result = remove_key_from_config(&mut config, "default", false);
assert!(matches!(&result, Err(CliError::Validation(_))));
assert!(config.keys.has_key("default"));
let err = result.unwrap_err().to_string();
assert!(err.contains(REMOVE_DEFAULT_ERROR));
}
#[test]
fn test_remove_default_with_force_removes_key() {
let mut config = Config::default();
let removed_default = remove_key_from_config(&mut config, "default", true)
.expect("forced default removal should succeed");
assert!(removed_default);
assert!(!config.keys.has_key("default"));
}
}