use anyhow::Result;
#[cfg(feature = "llm")]
use auto_gitmoji::matcher::llm::{LLMConfig, LLMModel, LLMProvider};
use auto_gitmoji::{commit::GitCommit, emoji::EmojiLookup, matcher::MatcherFactory};
use clap::Parser;
const RESET: &str = "\x1b[0m";
const BOLD: &str = "\x1b[1m";
const DIM: &str = "\x1b[2m";
const CYAN: &str = "\x1b[36m";
const GREEN: &str = "\x1b[32m";
const YELLOW: &str = "\x1b[33m";
const BLUE: &str = "\x1b[34m";
const MAGENTA: &str = "\x1b[35m";
const RED: &str = "\x1b[31m";
const BRIGHT_CYAN: &str = "\x1b[96m";
const BRIGHT_GREEN: &str = "\x1b[92m";
const BRIGHT_YELLOW: &str = "\x1b[93m";
#[derive(Parser)]
#[command(
name = "amoji",
version = "0.1.0",
author = "wty92911",
about = "Automatically prepend gitmoji to commit messages"
)]
struct Args {
message: Option<String>,
#[arg(long)]
#[arg(short)]
#[arg(default_value_t = false)]
dry_run: bool,
#[arg(long)]
#[arg(short)]
#[arg(default_value_t = false)]
show_emoji: bool,
#[arg(long)]
#[arg(short = 'm')]
#[arg(default_value_t = false)]
help_message: bool,
}
fn print_help_message() {
println!(
r#"
{BOLD}{CYAN}๐ Auto-Gitmoji (amoji) - Smart Commit Message Enhancement{RESET}
{BOLD}{YELLOW}USAGE EXAMPLES:{RESET}
{GREEN}amoji{RESET} {DIM}"{RESET}add new feature for users{DIM}"{RESET} {DIM}# โจ :sparkles: add new feature for users{RESET}
{GREEN}amoji{RESET} {DIM}"{RESET}fix login validation bug{DIM}"{RESET} {BLUE}--dry-run{RESET} {DIM}# Preview: ๐ :bug: fix login validation bug{RESET}
{GREEN}amoji{RESET} {BLUE}--show-emoji{RESET} {DIM}# List all available gitmojis{RESET}
{GREEN}amoji{RESET} {BLUE}--help-message{RESET} {DIM}# Show this help with examples{RESET}
{BOLD}{YELLOW}SUPPORTED COMMIT TYPES{RESET} {DIM}(partial list):{RESET}
{MAGENTA}โข{RESET} {BOLD}Features:{RESET} {CYAN}add, create, implement, introduce{RESET} โ โจ {DIM}:sparkles:{RESET}
{MAGENTA}โข{RESET} {BOLD}Bug Fixes:{RESET} {CYAN}fix, repair, resolve, correct{RESET} โ ๐ {DIM}:bug:{RESET}
{MAGENTA}โข{RESET} {BOLD}Hotfixes:{RESET} {CYAN}hotfix, urgent{RESET} โ ๐๏ธ {DIM}:ambulance:{RESET}
{MAGENTA}โข{RESET} {BOLD}Docs:{RESET} {CYAN}docs, documentation, readme{RESET} โ ๐ {DIM}:memo:{RESET}
{MAGENTA}โข{RESET} {BOLD}Refactoring:{RESET} {CYAN}refactor, restructure, cleanup{RESET} โ โป๏ธ {DIM}:recycle:{RESET}
{MAGENTA}โข{RESET} {BOLD}Performance:{RESET} {CYAN}optimize, performance, speed{RESET} โ โก {DIM}:zap:{RESET}
{MAGENTA}โข{RESET} {BOLD}Tests:{RESET} {CYAN}test, testing, spec{RESET} โ ๐งช {DIM}:test_tube:{RESET}
{MAGENTA}โข{RESET} {BOLD}Styling:{RESET} {CYAN}style, format, lint{RESET} โ ๐ {DIM}:lipstick:{RESET}
{MAGENTA}โข{RESET} {BOLD}Dependencies:{RESET} {CYAN}deps, dependency, package{RESET} โ ๐ฆ {DIM}:package:{RESET}
{MAGENTA}โข{RESET} {BOLD}Security:{RESET} {CYAN}security, vulnerability, auth{RESET} โ ๐ {DIM}:lock:{RESET}
{BOLD}{YELLOW}HOW IT WORKS:{RESET}
{BRIGHT_CYAN}1.{RESET} Analyzes the {BOLD}first word{RESET} of your commit message
{BRIGHT_CYAN}2.{RESET} Matches it against {BOLD}200+{RESET} keywords
{BRIGHT_CYAN}3.{RESET} Prepends the appropriate gitmoji
{BRIGHT_CYAN}4.{RESET} Executes: {GREEN}git commit -m{RESET} {DIM}":emoji: your message"{RESET}
{BOLD}{BRIGHT_YELLOW}๐ก TIP:{RESET} Use {BLUE}--dry-run{RESET} to preview before committing!
"#,
);
}
fn main() -> Result<()> {
let args = Args::parse();
if args.help_message {
print_help_message();
return Ok(());
}
if args.show_emoji {
println!("{BOLD}{CYAN}Available gitmoji codes:{RESET}");
let mut codes = EmojiLookup::all_codes();
codes.sort();
for code in codes {
if let Some(unicode) = EmojiLookup::code_to_unicode(code) {
println!(" {unicode} {DIM}{code}{RESET}");
}
}
return Ok(());
}
let message = args
.message
.ok_or_else(|| anyhow::anyhow!("Message argument is required"))?;
let matcher = if cfg!(feature = "llm") {
#[cfg(feature = "llm")]
{
MatcherFactory::llm_with_fallback(LLMConfig::from_env(
LLMProvider::SiliconFlow,
LLMModel::Qwen2_7bInstruct,
)?)
}
#[cfg(not(feature = "llm"))]
{
MatcherFactory::simple()
}
} else {
MatcherFactory::simple()
};
let match_result = matcher.match_emoji(&message)?;
if let Some((emoji_code, formatted_message)) = match_result {
let emoji_unicode = EmojiLookup::code_to_unicode(&emoji_code).unwrap_or("โ");
println!("{BOLD}{GREEN}๐ฏ Matched emoji:{RESET} {emoji_unicode} {DIM}{emoji_code}{RESET}",);
println!("{BOLD}{BLUE}๐ Full message:{RESET} {formatted_message}");
if !args.dry_run {
match GitCommit::has_staged_changes() {
Ok(true) => {
println!("{BRIGHT_GREEN}โ
Staged changes detected{RESET}");
}
Ok(false) => {
println!(
"{YELLOW}โ ๏ธ No staged changes found. Please stage your changes first with {BOLD}'git add'{RESET}",
);
return Ok(());
}
Err(e) => {
println!("{YELLOW}โ ๏ธ Could not check Git status: {e}{RESET}");
println!(" {DIM}Proceeding anyway...{RESET}");
}
}
}
match GitCommit::commit(&formatted_message, args.dry_run) {
Ok(result) => {
println!("{BRIGHT_GREEN}โ
{RESET} {result}");
}
Err(e) => {
eprintln!("{RED}โ Commit failed: {RESET} {e}");
std::process::exit(1);
}
}
} else {
eprintln!("{RED}โ Could not find appropriate emoji for message{RESET}",);
std::process::exit(1);
}
Ok(())
}