feat: use clap crate, add -m flag, change -d to --debug

This commit is contained in:
rzmk 2024-01-01 04:32:51 -05:00
parent 86ee66c7be
commit 07e03335cc
No known key found for this signature in database
6 changed files with 374 additions and 113 deletions

View file

@ -1,55 +1,53 @@
use inquire::{Confirm, Select};
use std::{env, process::Command};
use clap::Parser;
use inquire::{formatter::OptionFormatter, Confirm, InquireError, Select};
fn print_debug_info(output: std::process::Output) {
println!("Debug info:");
println!("stdout:\n{}", String::from_utf8_lossy(&output.stdout));
println!("stderr:\n{}", String::from_utf8_lossy(&output.stderr));
println!("Exit status:\n{}", output.status);
}
pub mod parser;
use parser::{Cli, CommitType};
pub mod util;
use util::orchestrate_commit;
fn main() {
// Check if -a flag is passed to run git add -A before commit
let args: Vec<String> = env::args().collect();
let run_git_add = args.len() > 1 && args.contains(&String::from("-a"));
// Check if -d or --dry-run flag is passed to run in dry run mode
let dry_run = args.len() > 1
&& (args.contains(&String::from("-d")) || args.contains(&String::from("--dry-run")));
if dry_run {
println!("Running in dry run mode\n");
}
// Check if -p flag is passed to run git push after commit
let run_git_push = args.len() > 1 && args.contains(&String::from("-p"));
// Check if --debug flag is passed to run in debug mode
let debug = args.len() > 1 && args.contains(&String::from("--debug"));
// Parse command line arguments
let cli: Cli = Cli::parse();
// Commit Type
// If a message was provided, run the commands in succession
if let Some(message) = &cli.message {
orchestrate_commit(&cli, &message);
return;
}
// Otherwise, prompt for a message
let commit_type_options = vec![
"build - build system and dependencies",
"ci - continuous integration",
"chore - misc/maintenance not related to core code",
"docs - documentation changes (e.g., README.md, comments)",
"feat - new feature or significant enhancement",
"fix - bug fix or error correction",
"perf - performance improvement",
"refactor - code restructuring or cleanup",
"test - add or update tests",
CommitType::new("build", "build system and dependencies"),
CommitType::new("ci", "continuous integration"),
CommitType::new("chore", "misc/maintenance not related to core code"),
CommitType::new("docs", "documentation changes (e.g., README.md, comments)"),
CommitType::new("feat", "new feature or significant enhancement"),
CommitType::new("fix", "bug fix or error correction"),
CommitType::new("perf", "performance improvement"),
CommitType::new("refactor", "code restructuring or cleanup"),
CommitType::new("test", "add or update tests"),
];
let commit_type = Select::new("Type:", commit_type_options).prompt();
let commit_type = match commit_type {
Ok(commit_type) =>
// Get the first word of the commit type
{
let commit_type = commit_type.split_whitespace().next().unwrap();
commit_type
}
Err(_) => {
println!("No commit type selected, exiting");
// Format the commit type options for display
let formatter: OptionFormatter<CommitType> =
&|ct| format!("{}: {}", ct.value.name, ct.value.description);
// Prompt for the commit type
let selected_type: Result<CommitType, InquireError> = Select::new("Type:", commit_type_options)
.with_formatter(formatter)
.prompt();
// Get the name of the selected commit type
let commit_type = match selected_type {
Ok(ct) => ct.name,
Err(e) => {
println!("Error: {:?}", e);
return;
}
};
// Summary
// Prompt for the commit summary (the message after the commit type)
let commit_summary = inquire::Text::new("Summary:").prompt();
let commit_summary = match commit_summary {
Ok(commit_summary) => commit_summary,
@ -59,59 +57,21 @@ fn main() {
}
};
// Format the commit message to include the commit type and summary
let result_message = format!("{}: {}", commit_type, commit_summary);
// Confirm
// Confirm the commit message
let confirm =
Confirm::new(format!("Result:\n\n{}\n\nCommit? (y/n):", result_message,).as_str()).prompt();
Confirm::new(format!("Result:\n\n{}\n\nCommit? (y/n):", result_message).as_str()).prompt();
// If confirmed, run the commands in succession
match confirm {
Ok(true) => {
if run_git_add {
println!("Running git add -A");
if !dry_run {
let output = Command::new("git")
.args(["add", "-A"])
.output()
.expect("failed to execute process");
if debug {
print_debug_info(output);
}
}
}
println!("Running git commit -m \"{}\"", result_message);
if !dry_run {
let output = Command::new("git")
.args(["commit", "-m", result_message.as_str()])
.output()
.expect("failed to execute process");
if debug {
print_debug_info(output);
}
}
if run_git_push {
println!("Running git push");
if !dry_run {
let output = Command::new("git")
.args(["push"])
.output()
.expect("failed to execute process");
if debug {
print_debug_info(output);
}
}
}
orchestrate_commit(&cli, &result_message);
}
_ => {
println!("Exiting");
return;
}
}
println!("Done 🎉");
}

50
src/parser.rs Normal file
View file

@ -0,0 +1,50 @@
use clap::Parser;
#[derive(Parser)]
#[command(
name = "commit-helper",
author = "Mueez Khan",
about = "Run multiple commands related to `git commit` in succession"
)]
pub struct Cli {
/// Run `git add -A` before committing
#[arg(short = 'a', long = "add")]
pub add: bool,
/// Run `git push` after committing
#[arg(short = 'p', long = "push")]
pub push: bool,
/// Run in debug mode (print output of each command)
#[arg(short = 'd', long = "debug")]
pub debug: bool,
/// Run in dry run mode (disallow executing commands)
#[arg(long = "dry-run")]
pub dry_run: bool,
/// Run `git commit` with the given message
/// (if not provided, will prompt for message)
#[arg(short = 'm', long = "message")]
pub message: Option<String>,
}
pub struct CommitType {
pub name: String,
pub description: String,
}
impl CommitType {
pub fn new(name: &str, description: &str) -> Self {
Self {
name: name.to_owned(),
description: description.to_owned(),
}
}
}
impl std::fmt::Display for CommitType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.name, self.description)
}
}

40
src/util.rs Normal file
View file

@ -0,0 +1,40 @@
use super::parser::Cli;
use std::process::Command;
// Run the commands in succession
pub fn orchestrate_commit(cli: &Cli, message: &str) {
if cli.add {
println!("Running git add -A");
run(&cli, "git", &["add", "-A"]);
}
println!("Running git commit -m \"{}\"", message);
run(&cli, "git", &["commit", "-m", message]);
if cli.push {
println!("Running git push");
run(&cli, "git", &["push"]);
}
println!("Done 🎉");
}
pub fn print_debug_info(output: std::process::Output) {
println!("Debug info:");
println!("stdout:\n{}", String::from_utf8_lossy(&output.stdout));
println!("stderr:\n{}", String::from_utf8_lossy(&output.stderr));
println!("Exit status:\n{}", output.status);
}
pub fn run(cli: &Cli, command: &str, args: &[&str]) {
if !cli.dry_run {
let output = Command::new(command)
.args(args)
.output()
.expect("failed to execute process");
if cli.debug {
print_debug_info(output);
}
}
}