1use crate::ProviderConfig;
2use crate::common::CommonParams;
3use crate::config::Config;
4use crate::instruction_presets::{
5 PresetType, get_instruction_preset_library, list_presets_formatted_by_type,
6};
7use crate::llm::get_available_provider_names;
8use crate::log_debug;
9use crate::ui;
10use anyhow::Context;
11use anyhow::{Result, anyhow};
12use colored::Colorize;
13use std::collections::HashMap;
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!(
25 "Starting 'config' command with common: {:?}, api_key: {:?}, model: {:?}, token_limit: {:?}, param: {:?}",
26 common,
27 api_key,
28 model,
29 token_limit,
30 param
31 );
32
33 let mut config = Config::load()?;
34 common.apply_to_config(&mut config)?;
35 let mut changes_made = false;
36
37 if let Some(provider) = common.provider {
38 if !get_available_provider_names()
39 .iter()
40 .any(|p| p == &provider)
41 {
42 return Err(anyhow!("Invalid provider: {}", provider));
43 }
44 if config.default_provider != provider {
45 config.default_provider.clone_from(&provider);
46 changes_made = true;
47 }
48 if !config.providers.contains_key(&provider) {
49 config
50 .providers
51 .insert(provider.clone(), ProviderConfig::default());
52 changes_made = true;
53 }
54 }
55
56 let provider_config = config
57 .providers
58 .get_mut(&config.default_provider)
59 .context("Could not get default provider")?;
60
61 if let Some(key) = api_key {
62 if provider_config.api_key != key {
63 provider_config.api_key = key;
64 changes_made = true;
65 }
66 }
67 if let Some(model) = model {
68 if provider_config.model != model {
69 provider_config.model = model;
70 changes_made = true;
71 }
72 }
73 if let Some(params) = param {
74 let additional_params = parse_additional_params(¶ms);
75 if provider_config.additional_params != additional_params {
76 provider_config.additional_params = additional_params;
77 changes_made = true;
78 }
79 }
80 if let Some(use_gitmoji) = common.gitmoji {
81 if config.use_gitmoji != use_gitmoji {
82 config.use_gitmoji = use_gitmoji;
83 changes_made = true;
84 }
85 }
86 if let Some(instr) = common.instructions {
87 if config.instructions != instr {
88 config.instructions = instr;
89 changes_made = true;
90 }
91 }
92 if let Some(limit) = token_limit {
93 if provider_config.token_limit != Some(limit) {
94 provider_config.token_limit = Some(limit);
95 changes_made = true;
96 }
97 }
98 if let Some(preset) = common.preset {
99 let preset_library = get_instruction_preset_library();
100 if preset_library.get_preset(&preset).is_some() {
101 if config.instruction_preset != preset {
102 config.instruction_preset = preset;
103 changes_made = true;
104 }
105 } else {
106 return Err(anyhow!("Invalid preset: {}", preset));
107 }
108 }
109
110 if changes_made {
111 config.save()?;
112 ui::print_success("Configuration updated successfully.");
113 println!();
114 }
115
116 print_configuration(&config);
118
119 Ok(())
120}
121
122fn print_configuration(config: &Config) {
124 println!(
126 "\n{}",
127 ui::create_gradient_text("🔮 Git-Iris Configuration 🔮").bold()
128 );
129 println!();
130
131 println!("{}", "Global Settings".bright_magenta().bold().underline());
133 println!();
134
135 let provider_label = "Default Provider:".bright_cyan().bold();
136 let provider_value = config.default_provider.bright_white();
137 println!(" {} {} {}", "🔹".cyan(), provider_label, provider_value);
138
139 let gitmoji_label = "Use Gitmoji:".bright_cyan().bold();
140 let gitmoji_value = if config.use_gitmoji {
141 "Yes".bright_green()
142 } else {
143 "No".bright_red()
144 };
145 println!(" {} {} {}", "🔹".cyan(), gitmoji_label, gitmoji_value);
146
147 let preset_label = "Instruction Preset:".bright_cyan().bold();
148 let preset_value = config.instruction_preset.bright_yellow();
149 println!(" {} {} {}", "🔹".cyan(), preset_label, preset_value);
150
151 println!();
152
153 if !config.instructions.is_empty() {
155 println!("{}", "Custom Instructions".bright_blue().bold().underline());
156 println!();
157
158 config.instructions.lines().for_each(|line| {
160 println!(" {}", line.bright_white().italic());
161 });
162
163 println!();
164 }
165
166 for (provider, provider_config) in &config.providers {
168 println!(
169 "{}",
170 format!("Provider: {provider}")
171 .bright_green()
172 .bold()
173 .underline()
174 );
175 println!();
176
177 let api_key_label = "API Key:".yellow().bold();
179 let api_key_value = if provider_config.api_key.is_empty() {
180 "Not set".bright_red().italic()
181 } else {
182 "Set ✓".bright_green()
183 };
184 println!(" {} {} {}", "🔒".yellow(), api_key_label, api_key_value);
185
186 let model_label = "Model:".yellow().bold();
188 let model_value = provider_config.model.bright_cyan();
189 println!(" {} {} {}", "✨".yellow(), model_label, model_value);
190
191 let token_limit_label = "Token Limit:".yellow().bold();
193 let token_limit_value = provider_config
194 .token_limit
195 .map_or("Default".bright_yellow(), |limit| {
196 limit.to_string().bright_white()
197 });
198 println!(
199 " {} {} {}",
200 "🔢".yellow(),
201 token_limit_label,
202 token_limit_value
203 );
204
205 if !provider_config.additional_params.is_empty() {
207 let params_label = "Additional Parameters:".yellow().bold();
208 println!(" {} {}", "🔧".yellow(), params_label);
209
210 for (key, value) in &provider_config.additional_params {
211 println!(" - {}: {}", key.bright_blue(), value.bright_white());
212 }
213 }
214
215 println!();
216 }
217}
218
219fn parse_additional_params(params: &[String]) -> HashMap<String, String> {
221 params
222 .iter()
223 .filter_map(|param| {
224 let parts: Vec<&str> = param.splitn(2, '=').collect();
225 if parts.len() == 2 {
226 Some((parts[0].to_string(), parts[1].to_string()))
227 } else {
228 None
229 }
230 })
231 .collect()
232}
233
234pub fn handle_list_presets_command() -> Result<()> {
236 let library = get_instruction_preset_library();
237
238 let both_presets = list_presets_formatted_by_type(&library, Some(PresetType::Both));
240 let commit_only_presets = list_presets_formatted_by_type(&library, Some(PresetType::Commit));
241 let review_only_presets = list_presets_formatted_by_type(&library, Some(PresetType::Review));
242
243 println!(
244 "{}",
245 "\nGit-Iris Instruction Presets\n".bright_magenta().bold()
246 );
247
248 println!(
249 "{}",
250 "General Presets (usable for both commit and review):"
251 .bright_cyan()
252 .bold()
253 );
254 println!("{both_presets}\n");
255
256 if !commit_only_presets.is_empty() {
257 println!("{}", "Commit-specific Presets:".bright_green().bold());
258 println!("{commit_only_presets}\n");
259 }
260
261 if !review_only_presets.is_empty() {
262 println!("{}", "Review-specific Presets:".bright_blue().bold());
263 println!("{review_only_presets}\n");
264 }
265
266 println!("{}", "Usage:".bright_yellow().bold());
267 println!(" git-iris gen --preset <preset-key>");
268 println!(" git-iris review --preset <preset-key>");
269 println!("\nPreset types: [B] = Both commands, [C] = Commit only, [R] = Review only");
270
271 Ok(())
272}