git_iris/
commands.rs

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/// Handle the 'config' command
16#[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(&params);
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 the configuration with beautiful styling
117    print_configuration(&config);
118
119    Ok(())
120}
121
122/// Display the configuration with beautiful styling and colors
123fn print_configuration(config: &Config) {
124    // Create a title with gradient
125    println!(
126        "\n{}",
127        ui::create_gradient_text("🔮 Git-Iris Configuration 🔮").bold()
128    );
129    println!();
130
131    // Global settings section
132    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    // Instructions section (if any)
154    if !config.instructions.is_empty() {
155        println!("{}", "Custom Instructions".bright_blue().bold().underline());
156        println!();
157
158        // Display full instructions, preserving newlines
159        config.instructions.lines().for_each(|line| {
160            println!("  {}", line.bright_white().italic());
161        });
162
163        println!();
164    }
165
166    // Provider configurations
167    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        // API Key status with lock emoji
178        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        // Model with sparkle emoji
187        let model_label = "Model:".yellow().bold();
188        let model_value = provider_config.model.bright_cyan();
189        println!("  {} {} {}", "✨".yellow(), model_label, model_value);
190
191        // Token limit with gauge emoji
192        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        // Additional parameters if any
206        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
219/// Parse additional parameters from the command line
220fn 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
234/// Handle the '`list_presets`' command
235pub fn handle_list_presets_command() -> Result<()> {
236    let library = get_instruction_preset_library();
237
238    // Get different categories of presets
239    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}