use std::io::{self, BufRead, Write};
use anyhow::Result;
use clap::Parser;
use crate::cli::atlassian::helpers::create_client;
#[derive(Parser)]
pub struct DeleteCommand {
pub key: String,
#[arg(long)]
pub force: bool,
}
impl DeleteCommand {
pub async fn execute(self) -> Result<()> {
let (client, _instance_url) = create_client()?;
if !self.force {
let issue = client.get_issue(&self.key).await?;
let prompt = format_delete_prompt(&self.key, &issue.summary);
if !confirm_with_reader(&prompt, &mut io::stdin().lock())? {
println!("Cancelled.");
return Ok(());
}
}
client.delete_issue(&self.key).await?;
println!("Deleted {}.", self.key);
Ok(())
}
}
fn format_delete_prompt(key: &str, summary: &str) -> String {
format!("Delete {key} ({summary})? [y/N] ")
}
fn confirm_with_reader(prompt: &str, reader: &mut dyn BufRead) -> Result<bool> {
print!("{prompt}");
io::stdout().flush()?;
let mut answer = String::new();
reader.read_line(&mut answer)?;
Ok(answer.trim().eq_ignore_ascii_case("y"))
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn delete_command_struct_fields() {
let cmd = DeleteCommand {
key: "PROJ-42".to_string(),
force: false,
};
assert_eq!(cmd.key, "PROJ-42");
assert!(!cmd.force);
}
#[test]
fn delete_command_force_mode() {
let cmd = DeleteCommand {
key: "PROJ-1".to_string(),
force: true,
};
assert!(cmd.force);
}
#[test]
fn format_prompt_includes_key_and_summary() {
let prompt = format_delete_prompt("PROJ-123", "Fix the bug");
assert_eq!(prompt, "Delete PROJ-123 (Fix the bug)? [y/N] ");
}
#[test]
fn format_prompt_with_empty_summary() {
let prompt = format_delete_prompt("PROJ-1", "");
assert_eq!(prompt, "Delete PROJ-1 ()? [y/N] ");
}
#[test]
fn format_prompt_with_special_chars() {
let prompt = format_delete_prompt("PROJ-99", "Fix \"quotes\" & <angles>");
assert!(prompt.contains("PROJ-99"));
assert!(prompt.contains("Fix \"quotes\" & <angles>"));
}
#[test]
fn confirm_yes_lowercase() {
let mut input = Cursor::new(b"y\n");
assert!(confirm_with_reader("Delete? ", &mut input).unwrap());
}
#[test]
fn confirm_yes_uppercase() {
let mut input = Cursor::new(b"Y\n");
assert!(confirm_with_reader("Delete? ", &mut input).unwrap());
}
#[test]
fn confirm_no() {
let mut input = Cursor::new(b"n\n");
assert!(!confirm_with_reader("Delete? ", &mut input).unwrap());
}
#[test]
fn confirm_empty_is_no() {
let mut input = Cursor::new(b"\n");
assert!(!confirm_with_reader("Delete? ", &mut input).unwrap());
}
#[test]
fn confirm_random_text_is_no() {
let mut input = Cursor::new(b"maybe\n");
assert!(!confirm_with_reader("Delete? ", &mut input).unwrap());
}
#[test]
fn confirm_yes_with_whitespace() {
let mut input = Cursor::new(b" y \n");
assert!(confirm_with_reader("Delete? ", &mut input).unwrap());
}
}