gitai/features/changelog/
cli.rs

1use super::change_log::ChangelogGenerator;
2use super::releasenotes::ReleaseNotesGenerator;
3use crate::common::{CommonParams, DetailLevel};
4use crate::config::Config;
5use crate::git::GitRepo;
6use crate::ui;
7use anyhow::{Context, Result};
8use colored::Colorize;
9use std::env;
10use std::str::FromStr;
11use std::sync::Arc;
12
13/// Handles the changelog generation command.
14///
15/// This function orchestrates the process of generating a changelog based on the provided
16/// parameters. It sets up the necessary environment, creates a `GitRepo` instance,
17/// and delegates the actual generation to the `ChangelogGenerator`.
18///
19/// # Arguments
20///
21/// * `common` - Common parameters for the command, including configuration overrides.
22/// * `from` - The starting point (commit or tag) for the changelog.
23/// * `to` - The ending point for the changelog. Defaults to "HEAD" if not provided.
24/// * `repository_url` - Optional URL of the remote repository to use.
25/// * `update_file` - Whether to update the changelog file.
26/// * `changelog_path` - Optional path to the changelog file.
27/// * `version_name` - Optional version name to use instead of extracting from Git refs.
28///
29/// # Returns
30///
31/// Returns a Result indicating success or containing an error if the operation failed.
32pub async fn handle_changelog_command(
33    common: CommonParams,
34    from: String,
35    to: Option<String>,
36    repository_url: Option<String>,
37    update_file: bool,
38    changelog_path: Option<String>,
39    version_name: Option<String>,
40) -> Result<()> {
41    // Load and apply configuration
42    let mut config = Config::load()?;
43    common.apply_to_config(&mut config)?;
44
45    // Create a spinner to indicate progress
46    let spinner = ui::create_spinner("Generating changelog...");
47
48    // Ensure we're in a git repository
49    if let Err(e) = config.check_environment() {
50        ui::print_error(&format!("Error: {e}"));
51        ui::print_info("\nPlease ensure the following:");
52        ui::print_info("1. Git is installed and accessible from the command line.");
53        ui::print_info(
54            "2. You are running this command from within a Git repository or provide a repository URL with --repo.",
55        );
56        ui::print_info("3. You have set up your configuration using 'git config'.");
57        return Err(e);
58    }
59
60    // Use the repository URL from command line or common params
61    let repo_url = repository_url.or(common.repository_url);
62
63    // Create a GitRepo instance based on the URL or current directory
64    let git_repo = if let Some(url) = repo_url {
65        Arc::new(GitRepo::clone_remote_repository(&url).context("Failed to clone repository")?)
66    } else {
67        let repo_path = env::current_dir()?;
68        Arc::new(GitRepo::new(&repo_path).context("Failed to create GitRepo")?)
69    };
70
71    // Keep a clone of the Arc for updating the changelog later if needed
72    let git_repo_for_update = Arc::clone(&git_repo);
73
74    // Set the default 'to' reference if not provided
75    let to = to.unwrap_or_else(|| "HEAD".to_string());
76
77    // Parse the detail level for the changelog
78    let detail_level = DetailLevel::from_str(&common.detail_level)?;
79
80    // Generate the changelog
81    let changelog =
82        ChangelogGenerator::generate(git_repo, &from, &to, &config, detail_level).await?;
83
84    // Clear the spinner and display the result
85    spinner.finish_and_clear();
86
87    // Output the changelog with decorative borders
88    ui::print_bordered_content(&changelog);
89
90    // Update the changelog file if requested
91    if update_file {
92        let path = changelog_path.unwrap_or_else(|| "CHANGELOG.md".to_string());
93        let update_spinner = ui::create_spinner(&format!("Updating changelog file at {path}..."));
94
95        match ChangelogGenerator::update_changelog_file(
96            &changelog,
97            &path,
98            &git_repo_for_update,
99            &to,
100            version_name,
101        ) {
102            Ok(()) => {
103                update_spinner.finish_and_clear();
104                ui::print_success(&format!(
105                    "✨ Changelog successfully updated at {}",
106                    path.bright_green()
107                ));
108            }
109            Err(e) => {
110                update_spinner.finish_and_clear();
111                ui::print_error(&format!("Failed to update changelog file: {e}"));
112            }
113        }
114    }
115
116    Ok(())
117}
118
119/// Handles the release notes generation command.
120///
121/// This function orchestrates the process of generating release notes based on the provided
122/// parameters. It sets up the necessary environment, creates a `GitRepo` instance,
123/// and delegates the actual generation to the `ReleaseNotesGenerator`.
124///
125/// # Arguments
126///
127/// * `common` - Common parameters for the command, including configuration overrides.
128/// * `from` - The starting point (commit or tag) for the release notes.
129/// * `to` - The ending point for the release notes. Defaults to "HEAD" if not provided.
130/// * `repository_url` - Optional URL of the remote repository to use.
131/// * `version_name` - Optional version name to use instead of extracting from Git refs.
132///
133/// # Returns
134///
135/// Returns a Result indicating success or containing an error if the operation failed.
136pub async fn handle_release_notes_command(
137    common: CommonParams,
138    from: String,
139    to: Option<String>,
140    repository_url: Option<String>,
141    version_name: Option<String>,
142) -> Result<()> {
143    // Load and apply configuration
144    let mut config = Config::load()?;
145    common.apply_to_config(&mut config)?;
146
147    // Create a spinner to indicate progress
148    let spinner = ui::create_spinner("Generating release notes...");
149
150    // Check environment prerequisites
151    if let Err(e) = config.check_environment() {
152        ui::print_error(&format!("Error: {e}"));
153        ui::print_info("\nPlease ensure the following:");
154        ui::print_info("1. Git is installed and accessible from the command line.");
155        ui::print_info(
156            "2. You are running this command from within a Git repository or provide a repository URL with --repo.",
157        );
158        ui::print_info("3. You have set up your configuration using 'git config'.");
159        return Err(e);
160    }
161
162    // Use the repository URL from command line or common params
163    let repo_url = repository_url.or(common.repository_url);
164
165    // Create a GitRepo instance based on the URL or current directory
166    let git_repo = if let Some(url) = repo_url {
167        Arc::new(GitRepo::clone_remote_repository(&url).context("Failed to clone repository")?)
168    } else {
169        let repo_path = env::current_dir()?;
170        Arc::new(GitRepo::new(&repo_path).context("Failed to create GitRepo")?)
171    };
172
173    // Set the default 'to' reference if not provided
174    let to = to.unwrap_or_else(|| "HEAD".to_string());
175
176    // Parse the detail level for the release notes
177    let detail_level = DetailLevel::from_str(&common.detail_level)?;
178
179    // Generate the release notes
180    let release_notes =
181        ReleaseNotesGenerator::generate(git_repo, &from, &to, &config, detail_level, version_name)
182            .await?;
183
184    // Clear the spinner and display the result
185    spinner.finish_and_clear();
186
187    // Output the release notes with decorative borders
188    ui::print_bordered_content(&release_notes);
189
190    Ok(())
191}