1use crate::common::CommonParams;
2use crate::config::Config;
3use crate::instruction_presets::get_instruction_preset_library;
4use crate::llm_providers::get_available_providers;
5use crate::log_debug;
6use crate::ui;
7use crate::ProviderConfig;
8use anyhow::Context;
9use anyhow::{anyhow, Result};
10use colored::Colorize;
11use std::collections::HashMap;
12
13use unicode_width::UnicodeWidthStr;
14
15#[allow(clippy::too_many_lines)]
17pub fn handle_config_command(
18 common: CommonParams,
19 api_key: Option<String>,
20 model: Option<String>,
21 token_limit: Option<usize>,
22 param: Option<Vec<String>>,
23) -> anyhow::Result<()> {
24 log_debug!("Starting 'config' command with common: {:?}, api_key: {:?}, model: {:?}, token_limit: {:?}, param: {:?}",
25 common, api_key, model, token_limit, param);
26
27 let mut config = Config::load()?;
28 common.apply_to_config(&mut config)?;
29 let mut changes_made = false;
30
31 if let Some(provider) = common.provider {
32 if !get_available_providers()
33 .iter()
34 .any(|p| p.to_string() == provider)
35 {
36 return Err(anyhow!("Invalid provider: {}", provider));
37 }
38 if config.default_provider != provider {
39 config.default_provider.clone_from(&provider);
40 changes_made = true;
41 }
42 if !config.providers.contains_key(&provider) {
43 config
44 .providers
45 .insert(provider.clone(), ProviderConfig::default());
46 changes_made = true;
47 }
48 }
49
50 let provider_config = config
51 .providers
52 .get_mut(&config.default_provider)
53 .context("Could not get default provider")?;
54
55 if let Some(key) = api_key {
56 if provider_config.api_key != key {
57 provider_config.api_key = key;
58 changes_made = true;
59 }
60 }
61 if let Some(model) = model {
62 if provider_config.model != model {
63 provider_config.model = model;
64 changes_made = true;
65 }
66 }
67 if let Some(params) = param {
68 let additional_params = parse_additional_params(¶ms);
69 if provider_config.additional_params != additional_params {
70 provider_config.additional_params = additional_params;
71 changes_made = true;
72 }
73 }
74 if let Some(use_gitmoji) = common.gitmoji {
75 if config.use_gitmoji != use_gitmoji {
76 config.use_gitmoji = use_gitmoji;
77 changes_made = true;
78 }
79 }
80 if let Some(instr) = common.instructions {
81 if config.instructions != instr {
82 config.instructions = instr;
83 changes_made = true;
84 }
85 }
86 if let Some(limit) = token_limit {
87 if provider_config.token_limit != Some(limit) {
88 provider_config.token_limit = Some(limit);
89 changes_made = true;
90 }
91 }
92 if let Some(preset) = common.preset {
93 let preset_library = get_instruction_preset_library();
94 if preset_library.get_preset(&preset).is_some() {
95 if config.instruction_preset != preset {
96 config.instruction_preset = preset;
97 changes_made = true;
98 }
99 } else {
100 return Err(anyhow!("Invalid preset: {}", preset));
101 }
102 }
103
104 if changes_made {
105 config.save()?;
106 ui::print_success("Configuration updated successfully.");
107 }
108
109 ui::print_info(&format!(
110 "Current configuration:\nDefault Provider: {}\nUse Gitmoji: {}\nInstructions: {}\nInstruction Preset: {}",
111 config.default_provider,
112 config.use_gitmoji,
113 if config.instructions.is_empty() {
114 "None".to_string()
115 } else {
116 config.instructions.replace('\n', ", ")
117 },
118 config.instruction_preset
119 ));
120 for (provider, provider_config) in &config.providers {
121 ui::print_info(&format!(
122 "\nProvider: {}\nAPI Key: {}\nModel: {}\nToken Limit: {}\nAdditional Parameters: {:?}",
123 provider,
124 if provider_config.api_key.is_empty() {
125 "Not set"
126 } else {
127 "Set"
128 },
129 provider_config.model,
130 provider_config
131 .token_limit
132 .map_or("Default".to_string(), |limit| limit.to_string()),
133 provider_config.additional_params
134 ));
135 }
136
137 Ok(())
138}
139
140fn parse_additional_params(params: &[String]) -> HashMap<String, String> {
142 params
143 .iter()
144 .filter_map(|param| {
145 let parts: Vec<&str> = param.splitn(2, '=').collect();
146 if parts.len() == 2 {
147 Some((parts[0].to_string(), parts[1].to_string()))
148 } else {
149 None
150 }
151 })
152 .collect()
153}
154
155pub fn handle_list_presets_command() -> Result<()> {
157 let preset_library = get_instruction_preset_library();
158
159 println!(
160 "{}",
161 "\nš® Available Instruction Presets š®"
162 .bright_purple()
163 .bold()
164 );
165 println!("{}", "ā".repeat(50).bright_purple());
166
167 let mut presets = preset_library.list_presets();
168 presets.sort_by(|a, b| a.0.cmp(b.0)); let max_key_length = presets
171 .iter()
172 .map(|(key, _)| key.width())
173 .max()
174 .unwrap_or(0);
175
176 for (key, preset) in presets {
177 println!(
178 "{} {:<width$} {}",
179 "ā¢".bright_cyan(),
180 key.bright_green().bold(),
181 preset.name.cyan().italic(),
182 width = max_key_length
183 );
184 println!(" {}", format!("\"{}\"", preset.description).bright_white());
185 println!(); }
187
188 println!("{}", "ā".repeat(50).bright_purple());
189 println!(
190 "{}",
191 "Use with: git-iris gen --preset <preset-name>"
192 .bright_yellow()
193 .italic()
194 );
195
196 Ok(())
197}