✨ feat(commands):为所有命令添加config_path参数支持,实现自定义配置文件路径
♻️ refactor(config):重构ConfigManager,添加with_path_fresh方法用于初始化新配置 🔧 fix(git):改进跨平台路径处理,增强git仓库检测的鲁棒性 ✅ test(tests):添加全面的集成测试,覆盖所有命令和跨平台场景
This commit is contained in:
@@ -13,13 +13,14 @@ use crate::i18n::{Messages, translate_changelog_category};
|
||||
|
||||
/// Generate changelog
|
||||
#[derive(Parser)]
|
||||
#[command(disable_version_flag = true, disable_help_flag = false)]
|
||||
pub struct ChangelogCommand {
|
||||
/// Output file path
|
||||
#[arg(short, long)]
|
||||
output: Option<PathBuf>,
|
||||
|
||||
/// Version to generate changelog for
|
||||
#[arg(short, long)]
|
||||
#[arg(long)]
|
||||
version: Option<String>,
|
||||
|
||||
/// Generate from specific tag
|
||||
@@ -51,7 +52,7 @@ pub struct ChangelogCommand {
|
||||
include_authors: bool,
|
||||
|
||||
/// Format (keep-a-changelog, github-releases)
|
||||
#[arg(short, long)]
|
||||
#[arg(long)]
|
||||
format: Option<String>,
|
||||
|
||||
/// Dry run (output to stdout)
|
||||
@@ -64,9 +65,13 @@ pub struct ChangelogCommand {
|
||||
}
|
||||
|
||||
impl ChangelogCommand {
|
||||
pub async fn execute(&self) -> Result<()> {
|
||||
pub async fn execute(&self, config_path: Option<PathBuf>) -> Result<()> {
|
||||
let repo = find_repo(std::env::current_dir()?.as_path())?;
|
||||
let manager = ConfigManager::new()?;
|
||||
let manager = if let Some(ref path) = config_path {
|
||||
ConfigManager::with_path(path)?
|
||||
} else {
|
||||
ConfigManager::new()?
|
||||
};
|
||||
let config = manager.config();
|
||||
let language = manager.get_language().unwrap_or(Language::English);
|
||||
let messages = Messages::new(language);
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::{bail, Context, Result};
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use dialoguer::{Confirm, Input, Select};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::{Language, manager::ConfigManager};
|
||||
use crate::config::CommitFormat;
|
||||
@@ -84,12 +85,16 @@ pub struct CommitCommand {
|
||||
}
|
||||
|
||||
impl CommitCommand {
|
||||
pub async fn execute(&self) -> Result<()> {
|
||||
pub async fn execute(&self, config_path: Option<PathBuf>) -> Result<()> {
|
||||
// Find git repository
|
||||
let repo = find_repo(std::env::current_dir()?.as_path())?;
|
||||
|
||||
// Load configuration
|
||||
let manager = ConfigManager::new()?;
|
||||
let manager = if let Some(ref path) = config_path {
|
||||
ConfigManager::with_path(path)?
|
||||
} else {
|
||||
ConfigManager::new()?
|
||||
};
|
||||
let config = manager.config();
|
||||
let language = manager.get_language().unwrap_or(Language::English);
|
||||
let messages = Messages::new(language);
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::{bail, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
use dialoguer::{Confirm, Input, Select};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::{Language, manager::ConfigManager};
|
||||
use crate::config::CommitFormat;
|
||||
@@ -191,43 +192,60 @@ enum ConfigSubcommand {
|
||||
|
||||
/// Test LLM connection
|
||||
TestLlm,
|
||||
|
||||
/// Show config file path
|
||||
Path,
|
||||
}
|
||||
|
||||
impl ConfigCommand {
|
||||
pub async fn execute(&self) -> Result<()> {
|
||||
pub async fn execute(&self, config_path: Option<PathBuf>) -> Result<()> {
|
||||
match &self.command {
|
||||
Some(ConfigSubcommand::Show) => self.show_config().await,
|
||||
Some(ConfigSubcommand::List) => self.list_config().await,
|
||||
Some(ConfigSubcommand::Edit) => self.edit_config().await,
|
||||
Some(ConfigSubcommand::Set { key, value }) => self.set_value(key, value).await,
|
||||
Some(ConfigSubcommand::Get { key }) => self.get_value(key).await,
|
||||
Some(ConfigSubcommand::SetLlm { provider }) => self.set_llm(provider.as_deref()).await,
|
||||
Some(ConfigSubcommand::SetOpenAiKey { key }) => self.set_openai_key(key).await,
|
||||
Some(ConfigSubcommand::SetAnthropicKey { key }) => self.set_anthropic_key(key).await,
|
||||
Some(ConfigSubcommand::SetKimiKey { key }) => self.set_kimi_key(key).await,
|
||||
Some(ConfigSubcommand::SetDeepSeekKey { key }) => self.set_deepseek_key(key).await,
|
||||
Some(ConfigSubcommand::SetOpenRouterKey { key }) => self.set_openrouter_key(key).await,
|
||||
Some(ConfigSubcommand::SetOllama { url, model }) => self.set_ollama(url.as_deref(), model.as_deref()).await,
|
||||
Some(ConfigSubcommand::SetKimi { base_url, model }) => self.set_kimi(base_url.as_deref(), model.as_deref()).await,
|
||||
Some(ConfigSubcommand::SetDeepSeek { base_url, model }) => self.set_deepseek(base_url.as_deref(), model.as_deref()).await,
|
||||
Some(ConfigSubcommand::SetOpenRouter { base_url, model }) => self.set_openrouter(base_url.as_deref(), model.as_deref()).await,
|
||||
Some(ConfigSubcommand::SetCommitFormat { format }) => self.set_commit_format(format).await,
|
||||
Some(ConfigSubcommand::SetVersionPrefix { prefix }) => self.set_version_prefix(prefix).await,
|
||||
Some(ConfigSubcommand::SetChangelogPath { path }) => self.set_changelog_path(path).await,
|
||||
Some(ConfigSubcommand::SetLanguage { language }) => self.set_language(language.as_deref()).await,
|
||||
Some(ConfigSubcommand::SetKeepTypesEnglish { keep }) => self.set_keep_types_english(*keep).await,
|
||||
Some(ConfigSubcommand::SetKeepChangelogTypesEnglish { keep }) => self.set_keep_changelog_types_english(*keep).await,
|
||||
Some(ConfigSubcommand::Reset { force }) => self.reset(*force).await,
|
||||
Some(ConfigSubcommand::Export { output }) => self.export_config(output.as_deref()).await,
|
||||
Some(ConfigSubcommand::Import { file }) => self.import_config(file).await,
|
||||
Some(ConfigSubcommand::ListModels) => self.list_models().await,
|
||||
Some(ConfigSubcommand::TestLlm) => self.test_llm().await,
|
||||
None => self.show_config().await,
|
||||
Some(ConfigSubcommand::Show) => self.show_config(&config_path).await,
|
||||
Some(ConfigSubcommand::List) => self.list_config(&config_path).await,
|
||||
Some(ConfigSubcommand::Edit) => self.edit_config(&config_path).await,
|
||||
Some(ConfigSubcommand::Set { key, value }) => self.set_value(key, value, &config_path).await,
|
||||
Some(ConfigSubcommand::Get { key }) => self.get_value(key, &config_path).await,
|
||||
Some(ConfigSubcommand::SetLlm { provider }) => self.set_llm(provider.as_deref(), &config_path).await,
|
||||
Some(ConfigSubcommand::SetOpenAiKey { key }) => self.set_openai_key(key, &config_path).await,
|
||||
Some(ConfigSubcommand::SetAnthropicKey { key }) => self.set_anthropic_key(key, &config_path).await,
|
||||
Some(ConfigSubcommand::SetKimiKey { key }) => self.set_kimi_key(key, &config_path).await,
|
||||
Some(ConfigSubcommand::SetDeepSeekKey { key }) => self.set_deepseek_key(key, &config_path).await,
|
||||
Some(ConfigSubcommand::SetOpenRouterKey { key }) => self.set_openrouter_key(key, &config_path).await,
|
||||
Some(ConfigSubcommand::SetOllama { url, model }) => self.set_ollama(url.as_deref(), model.as_deref(), &config_path).await,
|
||||
Some(ConfigSubcommand::SetKimi { base_url, model }) => self.set_kimi(base_url.as_deref(), model.as_deref(), &config_path).await,
|
||||
Some(ConfigSubcommand::SetDeepSeek { base_url, model }) => self.set_deepseek(base_url.as_deref(), model.as_deref(), &config_path).await,
|
||||
Some(ConfigSubcommand::SetOpenRouter { base_url, model }) => self.set_openrouter(base_url.as_deref(), model.as_deref(), &config_path).await,
|
||||
Some(ConfigSubcommand::SetCommitFormat { format }) => self.set_commit_format(format, &config_path).await,
|
||||
Some(ConfigSubcommand::SetVersionPrefix { prefix }) => self.set_version_prefix(prefix, &config_path).await,
|
||||
Some(ConfigSubcommand::SetChangelogPath { path }) => self.set_changelog_path(path, &config_path).await,
|
||||
Some(ConfigSubcommand::SetLanguage { language }) => self.set_language(language.as_deref(), &config_path).await,
|
||||
Some(ConfigSubcommand::SetKeepTypesEnglish { keep }) => self.set_keep_types_english(*keep, &config_path).await,
|
||||
Some(ConfigSubcommand::SetKeepChangelogTypesEnglish { keep }) => self.set_keep_changelog_types_english(*keep, &config_path).await,
|
||||
Some(ConfigSubcommand::Reset { force }) => self.reset(*force, &config_path).await,
|
||||
Some(ConfigSubcommand::Export { output }) => self.export_config(output.as_deref(), &config_path).await,
|
||||
Some(ConfigSubcommand::Import { file }) => self.import_config(file, &config_path).await,
|
||||
Some(ConfigSubcommand::ListModels) => self.list_models(&config_path).await,
|
||||
Some(ConfigSubcommand::TestLlm) => self.test_llm(&config_path).await,
|
||||
Some(ConfigSubcommand::Path) => self.show_path(&config_path).await,
|
||||
None => self.show_config(&config_path).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn show_config(&self) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
fn get_manager(&self, config_path: &Option<PathBuf>) -> Result<ConfigManager> {
|
||||
match config_path {
|
||||
Some(path) => ConfigManager::with_path(path),
|
||||
None => ConfigManager::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn show_path(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
println!("{}", manager.path().display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn show_config(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
let config = manager.config();
|
||||
|
||||
println!("{}", "\nQuiCommit Configuration".bold());
|
||||
@@ -306,8 +324,8 @@ impl ConfigCommand {
|
||||
}
|
||||
|
||||
/// List all configuration information with masked API keys
|
||||
async fn list_config(&self) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn list_config(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
let config = manager.config();
|
||||
|
||||
println!("{}", "\nQuiCommit Configuration".bold());
|
||||
@@ -404,15 +422,15 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn edit_config(&self) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn edit_config(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
crate::utils::editor::edit_file(manager.path())?;
|
||||
println!("{} Configuration updated", "✓".green());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_value(&self, key: &str, value: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_value(&self, key: &str, value: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
match key {
|
||||
"llm.provider" => manager.set_llm_provider(value.to_string()),
|
||||
@@ -450,8 +468,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_value(&self, key: &str) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn get_value(&self, key: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
let config = manager.config();
|
||||
|
||||
let value = match key {
|
||||
@@ -469,8 +487,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_llm(&self, provider: Option<&str>) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_llm(&self, provider: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
let provider = if let Some(p) = provider {
|
||||
p.to_string()
|
||||
@@ -602,48 +620,48 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_openai_key(&self, key: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_openai_key(&self, key: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_openai_api_key(key.to_string());
|
||||
manager.save()?;
|
||||
println!("{} OpenAI API key set", "✓".green());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_anthropic_key(&self, key: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_anthropic_key(&self, key: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_anthropic_api_key(key.to_string());
|
||||
manager.save()?;
|
||||
println!("{} Anthropic API key set", "✓".green());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_kimi_key(&self, key: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_kimi_key(&self, key: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_kimi_api_key(key.to_string());
|
||||
manager.save()?;
|
||||
println!("{} Kimi API key set", "✓".green());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_deepseek_key(&self, key: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_deepseek_key(&self, key: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_deepseek_api_key(key.to_string());
|
||||
manager.save()?;
|
||||
println!("{} DeepSeek API key set", "✓".green());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_openrouter_key(&self, key: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_openrouter_key(&self, key: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_openrouter_api_key(key.to_string());
|
||||
manager.save()?;
|
||||
println!("{} OpenRouter API key set", "✓".green());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_kimi(&self, base_url: Option<&str>, model: Option<&str>) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_kimi(&self, base_url: Option<&str>, model: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
if let Some(url) = base_url {
|
||||
manager.set_kimi_base_url(url.to_string());
|
||||
@@ -657,8 +675,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_deepseek(&self, base_url: Option<&str>, model: Option<&str>) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_deepseek(&self, base_url: Option<&str>, model: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
if let Some(url) = base_url {
|
||||
manager.set_deepseek_base_url(url.to_string());
|
||||
@@ -672,8 +690,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_openrouter(&self, base_url: Option<&str>, model: Option<&str>) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_openrouter(&self, base_url: Option<&str>, model: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
if let Some(url) = base_url {
|
||||
manager.set_openrouter_base_url(url.to_string());
|
||||
@@ -687,8 +705,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_ollama(&self, url: Option<&str>, model: Option<&str>) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_ollama(&self, url: Option<&str>, model: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
if let Some(u) = url {
|
||||
manager.config_mut().llm.ollama.url = u.to_string();
|
||||
@@ -702,8 +720,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_commit_format(&self, format: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_commit_format(&self, format: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
let format = match format {
|
||||
"conventional" => CommitFormat::Conventional,
|
||||
@@ -717,24 +735,24 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_version_prefix(&self, prefix: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_version_prefix(&self, prefix: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_version_prefix(prefix.to_string());
|
||||
manager.save()?;
|
||||
println!("{} Set version prefix to '{}'", "✓".green(), prefix);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_changelog_path(&self, path: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_changelog_path(&self, path: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_changelog_path(path.to_string());
|
||||
manager.save()?;
|
||||
println!("{} Set changelog path to {}", "✓".green(), path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_language(&self, language: Option<&str>) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_language(&self, language: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
let language_code = if let Some(lang) = language {
|
||||
lang.to_string()
|
||||
@@ -763,8 +781,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_keep_types_english(&self, keep: bool) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_keep_types_english(&self, keep: bool, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_keep_types_english(keep);
|
||||
manager.save()?;
|
||||
let status = if keep { "enabled" } else { "disabled" };
|
||||
@@ -772,8 +790,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_keep_changelog_types_english(&self, keep: bool) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_keep_changelog_types_english(&self, keep: bool, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.set_keep_changelog_types_english(keep);
|
||||
manager.save()?;
|
||||
let status = if keep { "enabled" } else { "disabled" };
|
||||
@@ -781,7 +799,7 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn reset(&self, force: bool) -> Result<()> {
|
||||
async fn reset(&self, force: bool, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
if !force {
|
||||
let confirm = Confirm::new()
|
||||
.with_prompt("Are you sure you want to reset all configuration?")
|
||||
@@ -794,7 +812,7 @@ impl ConfigCommand {
|
||||
}
|
||||
}
|
||||
|
||||
let mut manager = ConfigManager::new()?;
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.reset();
|
||||
manager.save()?;
|
||||
|
||||
@@ -802,8 +820,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn export_config(&self, output: Option<&str>) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn export_config(&self, output: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
let toml = manager.export()?;
|
||||
|
||||
if let Some(path) = output {
|
||||
@@ -816,10 +834,10 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn import_config(&self, file: &str) -> Result<()> {
|
||||
async fn import_config(&self, file: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let toml = std::fs::read_to_string(file)?;
|
||||
|
||||
let mut manager = ConfigManager::new()?;
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
manager.import(&toml)?;
|
||||
manager.save()?;
|
||||
|
||||
@@ -827,8 +845,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_models(&self) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn list_models(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
let config = manager.config();
|
||||
|
||||
match config.llm.provider.as_str() {
|
||||
@@ -984,8 +1002,8 @@ impl ConfigCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_llm(&self) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn test_llm(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
let config = manager.config();
|
||||
|
||||
println!("Testing LLM connection ({})...", config.llm.provider.cyan());
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use dialoguer::{Confirm, Input, Select};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::{GitProfile, Language};
|
||||
use crate::config::manager::ConfigManager;
|
||||
@@ -22,12 +23,13 @@ pub struct InitCommand {
|
||||
}
|
||||
|
||||
impl InitCommand {
|
||||
pub async fn execute(&self) -> Result<()> {
|
||||
// Start with English messages for initialization
|
||||
pub async fn execute(&self, config_path: Option<PathBuf>) -> Result<()> {
|
||||
let messages = Messages::new(Language::English);
|
||||
println!("{}", messages.initializing().bold().cyan());
|
||||
|
||||
let config_path = crate::config::AppConfig::default_path()?;
|
||||
let config_path = config_path.unwrap_or_else(|| {
|
||||
crate::config::AppConfig::default_path().unwrap()
|
||||
});
|
||||
|
||||
// Check if config already exists
|
||||
if config_path.exists() && !self.reset {
|
||||
@@ -41,20 +43,24 @@ impl InitCommand {
|
||||
println!("{}", "Initialization cancelled.".yellow());
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
println!("{}", "Configuration already exists. Use --reset to overwrite.".yellow());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut manager = if self.reset {
|
||||
ConfigManager::new()?
|
||||
} else {
|
||||
ConfigManager::new().or_else(|_| Ok::<_, anyhow::Error>(ConfigManager::default()))?
|
||||
};
|
||||
// Create parent directory if needed
|
||||
if let Some(parent) = config_path.parent() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to create config directory: {}", e))?;
|
||||
}
|
||||
|
||||
// Create new config manager with fresh config
|
||||
let mut manager = ConfigManager::with_path_fresh(&config_path)?;
|
||||
|
||||
if self.yes {
|
||||
// Quick setup with defaults
|
||||
self.quick_setup(&mut manager).await?;
|
||||
} else {
|
||||
// Interactive setup
|
||||
self.interactive_setup(&mut manager).await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::{bail, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
use dialoguer::{Confirm, Input, Select};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::manager::ConfigManager;
|
||||
use crate::config::{GitProfile, TokenConfig, TokenType};
|
||||
@@ -123,27 +124,34 @@ enum TokenSubcommand {
|
||||
}
|
||||
|
||||
impl ProfileCommand {
|
||||
pub async fn execute(&self) -> Result<()> {
|
||||
pub async fn execute(&self, config_path: Option<PathBuf>) -> Result<()> {
|
||||
match &self.command {
|
||||
Some(ProfileSubcommand::Add) => self.add_profile().await,
|
||||
Some(ProfileSubcommand::Remove { name }) => self.remove_profile(name).await,
|
||||
Some(ProfileSubcommand::List) => self.list_profiles().await,
|
||||
Some(ProfileSubcommand::Show { name }) => self.show_profile(name.as_deref()).await,
|
||||
Some(ProfileSubcommand::Edit { name }) => self.edit_profile(name).await,
|
||||
Some(ProfileSubcommand::SetDefault { name }) => self.set_default(name).await,
|
||||
Some(ProfileSubcommand::SetRepo { name }) => self.set_repo(name).await,
|
||||
Some(ProfileSubcommand::Apply { name, global }) => self.apply_profile(name.as_deref(), *global).await,
|
||||
Some(ProfileSubcommand::Switch) => self.switch_profile().await,
|
||||
Some(ProfileSubcommand::Copy { from, to }) => self.copy_profile(from, to).await,
|
||||
Some(ProfileSubcommand::Token { token_command }) => self.handle_token_command(token_command).await,
|
||||
Some(ProfileSubcommand::Check { name }) => self.check_profile(name.as_deref()).await,
|
||||
Some(ProfileSubcommand::Stats { name }) => self.show_stats(name.as_deref()).await,
|
||||
None => self.list_profiles().await,
|
||||
Some(ProfileSubcommand::Add) => self.add_profile(&config_path).await,
|
||||
Some(ProfileSubcommand::Remove { name }) => self.remove_profile(name, &config_path).await,
|
||||
Some(ProfileSubcommand::List) => self.list_profiles(&config_path).await,
|
||||
Some(ProfileSubcommand::Show { name }) => self.show_profile(name.as_deref(), &config_path).await,
|
||||
Some(ProfileSubcommand::Edit { name }) => self.edit_profile(name, &config_path).await,
|
||||
Some(ProfileSubcommand::SetDefault { name }) => self.set_default(name, &config_path).await,
|
||||
Some(ProfileSubcommand::SetRepo { name }) => self.set_repo(name, &config_path).await,
|
||||
Some(ProfileSubcommand::Apply { name, global }) => self.apply_profile(name.as_deref(), *global, &config_path).await,
|
||||
Some(ProfileSubcommand::Switch) => self.switch_profile(&config_path).await,
|
||||
Some(ProfileSubcommand::Copy { from, to }) => self.copy_profile(from, to, &config_path).await,
|
||||
Some(ProfileSubcommand::Token { token_command }) => self.handle_token_command(token_command, &config_path).await,
|
||||
Some(ProfileSubcommand::Check { name }) => self.check_profile(name.as_deref(), &config_path).await,
|
||||
Some(ProfileSubcommand::Stats { name }) => self.show_stats(name.as_deref(), &config_path).await,
|
||||
None => self.list_profiles(&config_path).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_profile(&self) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
fn get_manager(&self, config_path: &Option<PathBuf>) -> Result<ConfigManager> {
|
||||
match config_path {
|
||||
Some(path) => ConfigManager::with_path(path),
|
||||
None => ConfigManager::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_profile(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
println!("{}", "\nAdd new profile".bold());
|
||||
println!("{}", "─".repeat(40));
|
||||
@@ -244,8 +252,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_profile(&self, name: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn remove_profile(&self, name: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
if !manager.has_profile(name) {
|
||||
bail!("Profile '{}' not found", name);
|
||||
@@ -269,8 +277,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_profiles(&self) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn list_profiles(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
|
||||
let profiles = manager.list_profiles();
|
||||
|
||||
@@ -319,8 +327,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn show_profile(&self, name: Option<&str>) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn show_profile(&self, name: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
|
||||
let profile = if let Some(n) = name {
|
||||
manager.get_profile(n)
|
||||
@@ -380,8 +388,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn edit_profile(&self, name: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn edit_profile(&self, name: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
let profile = manager.get_profile(name)
|
||||
.ok_or_else(|| anyhow::anyhow!("Profile '{}' not found", name))?
|
||||
@@ -420,8 +428,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_default(&self, name: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_default(&self, name: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
manager.set_default_profile(Some(name.to_string()))?;
|
||||
manager.save()?;
|
||||
@@ -431,8 +439,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_repo(&self, name: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn set_repo(&self, name: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
let repo = find_repo(std::env::current_dir()?.as_path())?;
|
||||
|
||||
let repo_path = repo.path().to_string_lossy().to_string();
|
||||
@@ -453,8 +461,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn apply_profile(&self, name: Option<&str>, global: bool) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn apply_profile(&self, name: Option<&str>, global: bool, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
let profile_name = if let Some(n) = name {
|
||||
n.to_string()
|
||||
@@ -490,8 +498,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn switch_profile(&self) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn switch_profile(&self, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
let profiles: Vec<String> = manager.list_profiles()
|
||||
.into_iter()
|
||||
@@ -527,15 +535,15 @@ impl ProfileCommand {
|
||||
.interact()?;
|
||||
|
||||
if apply {
|
||||
self.apply_profile(Some(selected), false).await?;
|
||||
self.apply_profile(Some(selected), false, config_path).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn copy_profile(&self, from: &str, to: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn copy_profile(&self, from: &str, to: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
let source = manager.get_profile(from)
|
||||
.ok_or_else(|| anyhow::anyhow!("Profile '{}' not found", from))?
|
||||
@@ -555,16 +563,16 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_token_command(&self, cmd: &TokenSubcommand) -> Result<()> {
|
||||
async fn handle_token_command(&self, cmd: &TokenSubcommand, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
match cmd {
|
||||
TokenSubcommand::Add { profile, service } => self.add_token(profile, service).await,
|
||||
TokenSubcommand::Remove { profile, service } => self.remove_token(profile, service).await,
|
||||
TokenSubcommand::List { profile } => self.list_tokens(profile).await,
|
||||
TokenSubcommand::Add { profile, service } => self.add_token(profile, service, config_path).await,
|
||||
TokenSubcommand::Remove { profile, service } => self.remove_token(profile, service, config_path).await,
|
||||
TokenSubcommand::List { profile } => self.list_tokens(profile, config_path).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_token(&self, profile_name: &str, service: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn add_token(&self, profile_name: &str, service: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
if !manager.has_profile(profile_name) {
|
||||
bail!("Profile '{}' not found", profile_name);
|
||||
@@ -610,8 +618,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_token(&self, profile_name: &str, service: &str) -> Result<()> {
|
||||
let mut manager = ConfigManager::new()?;
|
||||
async fn remove_token(&self, profile_name: &str, service: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let mut manager = self.get_manager(config_path)?;
|
||||
|
||||
if !manager.has_profile(profile_name) {
|
||||
bail!("Profile '{}' not found", profile_name);
|
||||
@@ -635,8 +643,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_tokens(&self, profile_name: &str) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn list_tokens(&self, profile_name: &str, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
|
||||
let profile = manager.get_profile(profile_name)
|
||||
.ok_or_else(|| anyhow::anyhow!("Profile '{}' not found", profile_name))?;
|
||||
@@ -662,8 +670,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_profile(&self, name: Option<&str>) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn check_profile(&self, name: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
|
||||
let profile_name = if let Some(n) = name {
|
||||
n.to_string()
|
||||
@@ -695,8 +703,8 @@ impl ProfileCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn show_stats(&self, name: Option<&str>) -> Result<()> {
|
||||
let manager = ConfigManager::new()?;
|
||||
async fn show_stats(&self, name: Option<&str>, config_path: &Option<PathBuf>) -> Result<()> {
|
||||
let manager = self.get_manager(config_path)?;
|
||||
|
||||
if let Some(n) = name {
|
||||
let profile = manager.get_profile(n)
|
||||
|
||||
@@ -3,6 +3,7 @@ use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use dialoguer::{Confirm, Input, Select};
|
||||
use semver::Version;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::{Language, manager::ConfigManager};
|
||||
use crate::git::{find_repo, GitRepo};
|
||||
@@ -61,9 +62,13 @@ pub struct TagCommand {
|
||||
}
|
||||
|
||||
impl TagCommand {
|
||||
pub async fn execute(&self) -> Result<()> {
|
||||
pub async fn execute(&self, config_path: Option<PathBuf>) -> Result<()> {
|
||||
let repo = find_repo(std::env::current_dir()?.as_path())?;
|
||||
let manager = ConfigManager::new()?;
|
||||
let manager = if let Some(ref path) = config_path {
|
||||
ConfigManager::with_path(path)?
|
||||
} else {
|
||||
ConfigManager::new()?
|
||||
};
|
||||
let config = manager.config();
|
||||
let language = manager.get_language().unwrap_or(Language::English);
|
||||
let messages = Messages::new(language);
|
||||
|
||||
Reference in New Issue
Block a user