git_iris/
common.rs

1use crate::config::Config;
2use crate::instruction_presets::{PresetType, get_instruction_preset_library};
3use crate::llm;
4use anyhow::Result;
5use clap::Args;
6use std::str::FromStr;
7
8#[derive(Clone, Copy, PartialEq, Eq, Debug)]
9pub enum DetailLevel {
10    Minimal,
11    Standard,
12    Detailed,
13}
14
15impl FromStr for DetailLevel {
16    type Err = anyhow::Error;
17
18    fn from_str(s: &str) -> Result<Self, Self::Err> {
19        match s.to_lowercase().as_str() {
20            "minimal" => Ok(Self::Minimal),
21            "standard" => Ok(Self::Standard),
22            "detailed" => Ok(Self::Detailed),
23            _ => Err(anyhow::anyhow!("Invalid detail level: {}", s)),
24        }
25    }
26}
27
28impl DetailLevel {
29    pub fn as_str(&self) -> &'static str {
30        match self {
31            Self::Minimal => "minimal",
32            Self::Standard => "standard",
33            Self::Detailed => "detailed",
34        }
35    }
36}
37
38#[derive(Args, Clone, Default, Debug)]
39pub struct CommonParams {
40    /// Override default LLM provider
41    #[arg(long, help = "Override default LLM provider", value_parser = available_providers_parser)]
42    pub provider: Option<String>,
43
44    /// Custom instructions for this operation
45    #[arg(short, long, help = "Custom instructions for this operation")]
46    pub instructions: Option<String>,
47
48    /// Select an instruction preset
49    #[arg(
50        long,
51        help = "Select an instruction preset (use 'git-iris list-presets' to see available presets for commits and reviews)"
52    )]
53    pub preset: Option<String>,
54
55    /// Enable or disable Gitmoji
56    #[arg(long, help = "Enable or disable Gitmoji")]
57    pub gitmoji: Option<bool>,
58
59    /// Set the detail level
60    #[arg(
61        long,
62        help = "Set the detail level (minimal, standard, detailed)",
63        default_value = "standard"
64    )]
65    pub detail_level: String,
66
67    /// Repository URL to use instead of local repository
68    #[arg(
69        short = 'r',
70        long = "repo",
71        help = "Repository URL to use instead of local repository"
72    )]
73    pub repository_url: Option<String>,
74}
75
76impl CommonParams {
77    pub fn apply_to_config(&self, config: &mut Config) -> Result<()> {
78        if let Some(provider) = &self.provider {
79            // Convert "claude" to "anthropic" for backward compatibility
80            let provider_name = if provider.to_lowercase() == "claude" {
81                "anthropic".to_string()
82            } else {
83                provider.clone()
84            };
85            config.default_provider.clone_from(&provider_name);
86        }
87        if let Some(instructions) = &self.instructions {
88            config.set_temp_instructions(Some(instructions.clone()));
89        }
90        if let Some(preset) = &self.preset {
91            config.set_temp_preset(Some(preset.clone()));
92        }
93        if let Some(use_gitmoji) = self.gitmoji {
94            config.use_gitmoji = use_gitmoji;
95        }
96        Ok(())
97    }
98
99    /// Check if the provided preset is valid for the specified preset type
100    pub fn is_valid_preset_for_type(&self, preset_type: PresetType) -> bool {
101        if let Some(preset_name) = &self.preset {
102            let library = get_instruction_preset_library();
103            let valid_presets = library.list_valid_presets_for_command(preset_type);
104            return valid_presets.iter().any(|(key, _)| *key == preset_name);
105        }
106        true // No preset specified is always valid
107    }
108}
109
110/// Validates that a provider name is available in the system
111pub fn available_providers_parser(s: &str) -> Result<String, String> {
112    let mut provider_name = s.to_lowercase();
113
114    // Handle legacy "claude" provider name by mapping it to "anthropic"
115    if provider_name == "claude" {
116        provider_name = "anthropic".to_string();
117    }
118
119    let available_providers = llm::get_available_provider_names();
120
121    if available_providers
122        .iter()
123        .any(|p| p.to_lowercase() == provider_name)
124    {
125        Ok(provider_name)
126    } else {
127        Err(format!(
128            "Invalid provider '{}'. Available providers: {}",
129            s,
130            available_providers.join(", ")
131        ))
132    }
133}
134
135pub fn get_combined_instructions(config: &Config) -> String {
136    let mut prompt = String::from("\n\n");
137
138    if !config.instructions.is_empty() {
139        prompt.push_str(&format!(
140            "\n\nAdditional instructions for the request:\n{}\n\n",
141            config.instructions
142        ));
143    }
144
145    let preset_library = get_instruction_preset_library();
146    if let Some(preset_instructions) = preset_library.get_preset(config.instruction_preset.as_str())
147    {
148        prompt.push_str(&format!(
149            "\n\nIMPORTANT: Use this style for your output:\n{}\n\n",
150            preset_instructions.instructions
151        ));
152    }
153
154    prompt
155}