git_iris/commit/
cli.rs

1use super::format_commit_result;
2use super::service::IrisCommitService;
3use super::types::format_commit_message;
4use crate::common::CommonParams;
5use crate::config::Config;
6use crate::git::GitRepo;
7use crate::instruction_presets::PresetType;
8use crate::messages;
9use crate::tui::run_tui_commit;
10use crate::ui;
11use anyhow::{Context, Result};
12use std::sync::Arc;
13
14#[allow(clippy::fn_params_excessive_bools)] // its ok to use multiple bools here
15#[allow(clippy::too_many_lines)] // Function is slightly over the line limit but still readable
16pub async fn handle_gen_command(
17    common: CommonParams,
18    auto_commit: bool,
19    use_gitmoji: bool,
20    print: bool,
21    verify: bool,
22    repository_url: Option<String>,
23) -> Result<()> {
24    // Check if the preset is appropriate for commit messages
25    if !common.is_valid_preset_for_type(PresetType::Commit) {
26        ui::print_warning(
27            "The specified preset may not be suitable for commit messages. Consider using a commit or general preset instead.",
28        );
29        ui::print_info("Run 'git-iris list-presets' to see available presets for commits.");
30    }
31
32    let mut config = Config::load()?;
33    common.apply_to_config(&mut config)?;
34
35    // Combine repository URL from CLI and CommonParams
36    let repo_url = repository_url.or(common.repository_url.clone());
37
38    // Create the git repository
39    let git_repo = GitRepo::new_from_url(repo_url).context("Failed to create GitRepo")?;
40
41    let repo_path = git_repo.repo_path().clone();
42    let provider_name = &config.default_provider;
43
44    let service = Arc::new(
45        IrisCommitService::new(
46            config.clone(),
47            &repo_path,
48            provider_name,
49            use_gitmoji && config.use_gitmoji,
50            verify,
51            git_repo,
52        )
53        .context("Failed to create IrisCommitService")?,
54    );
55
56    // Check environment prerequisites
57    if let Err(e) = service.check_environment() {
58        ui::print_error(&format!("Error: {e}"));
59        ui::print_info("\nPlease ensure the following:");
60        ui::print_info("1. Git is installed and accessible from the command line.");
61        ui::print_info(
62            "2. You are running this command from within a Git repository or provide a repository URL with --repo.",
63        );
64        ui::print_info("3. You have set up your configuration using 'git-iris config'.");
65        return Err(e);
66    }
67
68    let git_info = service.get_git_info().await?;
69
70    if git_info.staged_files.is_empty() {
71        ui::print_warning(
72            "No staged changes. Please stage your changes before generating a commit message.",
73        );
74        ui::print_info("You can stage changes using 'git add <file>' or 'git add .'");
75        return Ok(());
76    }
77
78    // Run pre-commit hook before we do anything else
79    if let Err(e) = service.pre_commit() {
80        ui::print_error(&format!("Pre-commit failed: {e}"));
81        return Err(e);
82    }
83
84    let effective_instructions = common
85        .instructions
86        .unwrap_or_else(|| config.instructions.clone());
87    let preset_str = common.preset.as_deref().unwrap_or("");
88
89    // Create and start the spinner
90    let spinner = ui::create_spinner("");
91    let random_message = messages::get_waiting_message();
92    spinner.set_message(random_message.text);
93
94    // Generate an initial message
95    let initial_message = service
96        .generate_message(preset_str, &effective_instructions)
97        .await?;
98
99    // Stop the spinner
100    spinner.finish_and_clear();
101
102    if print {
103        println!("{}", format_commit_message(&initial_message));
104        return Ok(());
105    }
106
107    if auto_commit {
108        // Only allow auto-commit for local repositories
109        if service.is_remote_repository() {
110            ui::print_error(
111                "Cannot automatically commit to a remote repository. Use --print instead.",
112            );
113            return Err(anyhow::anyhow!(
114                "Auto-commit not supported for remote repositories"
115            ));
116        }
117
118        match service.perform_commit(&format_commit_message(&initial_message)) {
119            Ok(result) => {
120                let output =
121                    format_commit_result(&result, &format_commit_message(&initial_message));
122                println!("{output}");
123            }
124            Err(e) => {
125                eprintln!("Failed to commit: {e}");
126                return Err(e);
127            }
128        }
129        return Ok(());
130    }
131
132    // Only allow interactive commit for local repositories
133    if service.is_remote_repository() {
134        ui::print_warning(
135            "Interactive commit not available for remote repositories. Using print mode instead.",
136        );
137        println!("{}", format_commit_message(&initial_message));
138        return Ok(());
139    }
140
141    run_tui_commit(
142        vec![initial_message],
143        effective_instructions,
144        String::from(preset_str),
145        git_info.user_name,
146        git_info.user_email,
147        service,
148        config.use_gitmoji,
149    )
150    .await?;
151
152    Ok(())
153}