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
68impl CommonParams {
69    pub fn apply_to_config(&self, config: &mut Config) -> Result<()> {
70        if let Some(provider) = &self.provider {
71            // Convert "claude" to "anthropic" for backward compatibility
72            let provider_name = if provider.to_lowercase() == "claude" {
73                "anthropic".to_string()
74            } else {
75                provider.clone()
76            };
77            config.default_provider.clone_from(&provider_name);
78        }
79        if let Some(instructions) = &self.instructions {
80            config.set_temp_instructions(Some(instructions.clone()));
81        }
82        if let Some(preset) = &self.preset {
83            config.set_temp_preset(Some(preset.clone()));
84        }
85        if let Some(use_gitmoji) = self.gitmoji {
86            config.use_gitmoji = use_gitmoji;
87        }
88        Ok(())
89    }
90
91    /// Check if the provided preset is valid for the specified preset type
92    pub fn is_valid_preset_for_type(&self, preset_type: PresetType) -> bool {
93        if let Some(preset_name) = &self.preset {
94            let library = get_instruction_preset_library();
95            let valid_presets = library.list_valid_presets_for_command(preset_type);
96            return valid_presets.iter().any(|(key, _)| *key == preset_name);
97        }
98        true // No preset specified is always valid
99    }
100}
101
102/// Validates that a provider name is available in the system
103pub fn available_providers_parser(s: &str) -> Result<String, String> {
104    let mut provider_name = s.to_lowercase();
105
106    // Handle legacy "claude" provider name by mapping it to "anthropic"
107    if provider_name == "claude" {
108        provider_name = "anthropic".to_string();
109    }
110
111    let available_providers = llm::get_available_provider_names();
112
113    if available_providers
114        .iter()
115        .any(|p| p.to_lowercase() == provider_name)
116    {
117        Ok(provider_name)
118    } else {
119        Err(format!(
120            "Invalid provider '{}'. Available providers: {}",
121            s,
122            available_providers.join(", ")
123        ))
124    }
125}
126
127pub fn get_combined_instructions(config: &Config) -> String {
128    let mut prompt = String::from("\n\n");
129
130    if !config.instructions.is_empty() {
131        prompt.push_str(&format!(
132            "\n\nAdditional instructions for the request:\n{}\n\n",
133            config.instructions
134        ));
135    }
136
137    let preset_library = get_instruction_preset_library();
138    if let Some(preset_instructions) = preset_library.get_preset(config.instruction_preset.as_str())
139    {
140        prompt.push_str(&format!(
141            "\n\nIMPORTANT: Use this style for your output:\n{}\n\n",
142            preset_instructions.instructions
143        ));
144    }
145
146    prompt
147}