Initial commit: git-issue-commit CLI tool
Some checks failed
CI / test (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
2026-01-29 19:59:24 +00:00
parent c1770f58b9
commit be89dfc4e7

120
src/interactive/mod.rs Normal file
View File

@@ -0,0 +1,120 @@
use tokio::io::{self, AsyncWriteExt, BufReader};
use anyhow::Result;
use crate::generator::MessageGenerator;
pub async fn run_interactive_mode(mut generator: MessageGenerator) -> Result<MessageGenerator> {
let types = vec!["feat", "fix", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore", "revert"];
generator.set_type(prompt_type(&types).await?);
generator.set_scope_opt(prompt_scope().await?);
generator.set_description(prompt_description().await?);
generator.set_body_opt(prompt_body().await?);
let is_breaking = prompt_breaking().await?;
generator.set_breaking(is_breaking);
if is_breaking {
generator.set_breaking_description(prompt_breaking_description().await?);
}
println!("\nGenerated commit message:");
println!("{}", generator.format_message());
Ok(generator)
}
async fn prompt_type(types: &[&str]) -> Result<String> {
let mut stdout = io::stdout();
println!("Select commit type:");
for (i, t) in types.iter().enumerate() {
println!(" {} - {}", i, t);
}
print!("Enter number (0-{}): ", types.len() - 1);
stdout.flush().await?;
let mut input = String::new();
let mut stdin = BufReader::new(io::stdin());
stdin.read_line(&mut input).await?;
let idx: Result<usize, _> = input.trim().parse();
match idx {
Ok(i) if i < types.len() => Ok(types[i].to_string()),
Ok(_) => {
eprintln!("Invalid selection, using default 'chore'");
Ok("chore".to_string())
}
Err(_) => {
eprintln!("Invalid input, using default 'chore'");
Ok("chore".to_string())
}
}
}
async fn prompt_scope() -> Result<Option<String>> {
let mut stdout = io::stdout();
let mut stdin = BufReader::new(io::stdin());
print!("Scope (optional): ");
stdout.flush().await?;
let mut input = String::new();
stdin.read_line(&mut input).await?;
let trimmed = input.trim().to_string();
if trimmed.is_empty() {
Ok(None)
} else {
Ok(Some(trimmed))
}
}
async fn prompt_description() -> Result<String> {
let mut stdout = io::stdout();
let mut stdin = BufReader::new(io::stdin());
print!("Commit description: ");
stdout.flush().await?;
let mut input = String::new();
stdin.read_line(&mut input).await?;
Ok(input.trim().to_string())
}
async fn prompt_body() -> Result<Option<String>> {
let mut stdout = io::stdout();
let mut stdin = BufReader::new(io::stdin());
print!("Extended description (optional, leave empty to skip): ");
stdout.flush().await?;
let mut input = String::new();
stdin.read_line(&mut input).await?;
let trimmed = input.trim().to_string();
if trimmed.is_empty() {
Ok(None)
} else {
Ok(Some(trimmed))
}
}
async fn prompt_breaking() -> Result<bool> {
let mut stdout = io::stdout();
let mut stdin = BufReader::new(io::stdin());
print!("Is this a breaking change? [y/N]: ");
stdout.flush().await?;
let mut input = String::new();
stdin.read_line(&mut input).await?;
Ok(input.trim().eq_ignore_ascii_case("y"))
}
async fn prompt_breaking_description() -> Result<String> {
let mut stdout = io::stdout();
let mut stdin = BufReader::new(io::stdin());
print!("Describe the breaking change: ");
stdout.flush().await?;
let mut input = String::new();
stdin.read_line(&mut input).await?;
Ok(input.trim().to_string())
}