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#[allow(clippy::struct_excessive_bools)]
8#[derive(Args, Clone, Default, Debug)]
9pub struct CommonParams {
10 #[arg(long, help = "Override default LLM provider", value_parser = available_providers_parser)]
12 pub provider: Option<String>,
13
14 #[arg(long, help = "Override model for this operation")]
16 pub model: Option<String>,
17
18 #[arg(short, long, help = "Custom instructions for this operation")]
20 pub instructions: Option<String>,
21
22 #[arg(
24 long,
25 help = "Select an instruction preset (use 'git-iris list-presets' to see available presets for commits and reviews)"
26 )]
27 pub preset: Option<String>,
28
29 #[arg(
31 long = "gitmoji",
32 help = "Enable Gitmoji",
33 conflicts_with = "no_gitmoji",
34 action = clap::ArgAction::SetTrue
35 )]
36 pub gitmoji_flag: bool,
37
38 #[arg(long = "no-gitmoji", help = "Disable Gitmoji", action = clap::ArgAction::SetTrue)]
40 pub no_gitmoji: bool,
41
42 #[arg(
44 long = "critic",
45 help = "Enable critic verification pass",
46 conflicts_with = "no_critic",
47 action = clap::ArgAction::SetTrue
48 )]
49 pub critic_flag: bool,
50
51 #[arg(long = "no-critic", help = "Disable critic verification pass", action = clap::ArgAction::SetTrue)]
53 pub no_critic: bool,
54
55 #[arg(skip)]
57 pub gitmoji: Option<bool>,
58
59 #[arg(skip)]
61 pub critic: Option<bool>,
62
63 #[arg(
65 short = 'r',
66 long = "repo",
67 help = "Repository URL to use instead of local repository"
68 )]
69 pub repository_url: Option<String>,
70}
71
72impl CommonParams {
73 #[must_use]
76 pub fn resolved_gitmoji(&self) -> Option<bool> {
77 if self.gitmoji_flag {
78 Some(true)
79 } else if self.no_gitmoji {
80 Some(false)
81 } else {
82 self.gitmoji }
84 }
85
86 #[must_use]
89 pub fn resolved_critic(&self) -> Option<bool> {
90 if self.critic_flag {
91 Some(true)
92 } else if self.no_critic {
93 Some(false)
94 } else {
95 self.critic
96 }
97 }
98
99 pub fn apply_to_config(&self, config: &mut Config) -> Result<bool> {
104 let mut changes_made = false;
105
106 if let Some(provider_str) = &self.provider {
107 let provider: Provider = provider_str.parse()?;
109 let provider_name = provider.name().to_string();
110
111 if config.default_provider != provider_name {
113 if !config.providers.contains_key(&provider_name) {
115 config.providers.insert(
116 provider_name.clone(),
117 ProviderConfig::with_defaults(provider),
118 );
119 }
120
121 config.default_provider = provider_name;
122 changes_made = true;
123 }
124 }
125
126 if let Some(model) = &self.model {
128 let provider_name = config.default_provider.clone();
129 if let Some(provider_config) = config.providers.get_mut(&provider_name) {
130 provider_config.model.clone_from(model);
131 changes_made = true;
132 }
133 }
134
135 if let Some(instructions) = &self.instructions {
136 config.set_temp_instructions(Some(instructions.clone()));
137 }
138
139 if let Some(preset) = &self.preset {
140 config.set_temp_preset(Some(preset.clone()));
141 }
142
143 if let Some(use_gitmoji) = self.resolved_gitmoji() {
145 config.gitmoji_override = Some(use_gitmoji);
146 if config.use_gitmoji != use_gitmoji {
147 config.use_gitmoji = use_gitmoji;
148 changes_made = true;
149 }
150 }
151
152 if let Some(critic_enabled) = self.resolved_critic()
153 && config.critic_enabled != critic_enabled
154 {
155 config.critic_enabled = critic_enabled;
156 changes_made = true;
157 }
158
159 Ok(changes_made)
160 }
161
162 #[must_use]
164 pub fn is_valid_preset_for_type(&self, preset_type: PresetType) -> bool {
165 if let Some(preset_name) = &self.preset {
166 let library = get_instruction_preset_library();
167 let valid_presets = library.list_valid_presets_for_command(preset_type);
168 return valid_presets.iter().any(|(key, _)| *key == preset_name);
169 }
170 true }
172}
173
174pub fn available_providers_parser(s: &str) -> Result<String, String> {
180 match s.parse::<Provider>() {
181 Ok(provider) => Ok(provider.name().to_string()),
182 Err(_) => Err(format!(
183 "Invalid provider '{}'. Available providers: {}",
184 s,
185 Provider::all_names().join(", ")
186 )),
187 }
188}