git_iris/
common.rs

1use crate::config::Config;
2use crate::instruction_presets::{PresetType, get_instruction_preset_library};
3use crate::providers::{Provider, ProviderConfig};
4use anyhow::Result;
5use clap::Args;
6
7#[derive(Args, Clone, Default, Debug)]
8pub struct CommonParams {
9    /// Override default LLM provider
10    #[arg(long, help = "Override default LLM provider", value_parser = available_providers_parser)]
11    pub provider: Option<String>,
12
13    /// Override model for this operation
14    #[arg(long, help = "Override model for this operation")]
15    pub model: Option<String>,
16
17    /// Custom instructions for this operation
18    #[arg(short, long, help = "Custom instructions for this operation")]
19    pub instructions: Option<String>,
20
21    /// Select an instruction preset
22    #[arg(
23        long,
24        help = "Select an instruction preset (use 'git-iris list-presets' to see available presets for commits and reviews)"
25    )]
26    pub preset: Option<String>,
27
28    /// Enable Gitmoji (default: true)
29    #[arg(
30        long = "gitmoji",
31        help = "Enable Gitmoji",
32        conflicts_with = "no_gitmoji",
33        action = clap::ArgAction::SetTrue
34    )]
35    pub gitmoji_flag: bool,
36
37    /// Disable Gitmoji
38    #[arg(long = "no-gitmoji", help = "Disable Gitmoji", action = clap::ArgAction::SetTrue)]
39    pub no_gitmoji: bool,
40
41    /// Internal: resolved gitmoji value (Some(true), Some(false), or None)
42    #[arg(skip)]
43    pub gitmoji: Option<bool>,
44
45    /// Repository URL to use instead of local repository
46    #[arg(
47        short = 'r',
48        long = "repo",
49        help = "Repository URL to use instead of local repository"
50    )]
51    pub repository_url: Option<String>,
52}
53
54impl CommonParams {
55    /// Get the resolved gitmoji value from CLI flags
56    /// Returns Some(true) for --gitmoji, Some(false) for --no-gitmoji, None if neither specified
57    #[must_use]
58    pub fn resolved_gitmoji(&self) -> Option<bool> {
59        if self.gitmoji_flag {
60            Some(true)
61        } else if self.no_gitmoji {
62            Some(false)
63        } else {
64            self.gitmoji // fallback to any programmatically set value
65        }
66    }
67
68    pub fn apply_to_config(&self, config: &mut Config) -> Result<bool> {
69        let mut changes_made = false;
70
71        if let Some(provider_str) = &self.provider {
72            // Parse and validate provider
73            let provider: Provider = provider_str.parse()?;
74            let provider_name = provider.name().to_string();
75
76            // Check if we need to update the default provider
77            if config.default_provider != provider_name {
78                // Ensure the provider exists in the providers HashMap
79                if !config.providers.contains_key(&provider_name) {
80                    config.providers.insert(
81                        provider_name.clone(),
82                        ProviderConfig::with_defaults(provider),
83                    );
84                }
85
86                config.default_provider = provider_name;
87                changes_made = true;
88            }
89        }
90
91        // Apply model override if specified
92        if let Some(model) = &self.model {
93            let provider_name = config.default_provider.clone();
94            if let Some(provider_config) = config.providers.get_mut(&provider_name) {
95                provider_config.model.clone_from(model);
96                changes_made = true;
97            }
98        }
99
100        if let Some(instructions) = &self.instructions {
101            config.set_temp_instructions(Some(instructions.clone()));
102        }
103
104        if let Some(preset) = &self.preset {
105            config.set_temp_preset(Some(preset.clone()));
106        }
107
108        // Track whether gitmoji was explicitly set via CLI (for style detection)
109        if let Some(use_gitmoji) = self.resolved_gitmoji() {
110            config.gitmoji_override = Some(use_gitmoji);
111            if config.use_gitmoji != use_gitmoji {
112                config.use_gitmoji = use_gitmoji;
113                changes_made = true;
114            }
115        }
116
117        Ok(changes_made)
118    }
119
120    /// Check if the provided preset is valid for the specified preset type
121    pub fn is_valid_preset_for_type(&self, preset_type: PresetType) -> bool {
122        if let Some(preset_name) = &self.preset {
123            let library = get_instruction_preset_library();
124            let valid_presets = library.list_valid_presets_for_command(preset_type);
125            return valid_presets.iter().any(|(key, _)| *key == preset_name);
126        }
127        true // No preset specified is always valid
128    }
129}
130
131/// Validates that a provider name is available in the system
132pub fn available_providers_parser(s: &str) -> Result<String, String> {
133    match s.parse::<Provider>() {
134        Ok(provider) => Ok(provider.name().to_string()),
135        Err(_) => Err(format!(
136            "Invalid provider '{}'. Available providers: {}",
137            s,
138            Provider::all_names().join(", ")
139        )),
140    }
141}