use clap::{Parser, Subcommand};
use std::io::{self, BufRead, Write};
use thiserror::Error;
#[derive(Debug, Parser)]
#[command(name = "rust-bucket")]
#[command(about = "Rust-first project bootstrapper for AI-first engineering")]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
Apply {
#[arg(long)]
force: bool,
},
}
#[derive(Debug, Error)]
pub enum CliError {
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("Invalid input: {0}")]
InvalidInput(String),
}
pub fn prompt_test_timeout() -> Result<u32, CliError> {
let stdout = io::stdout();
let mut handle = stdout.lock();
write!(handle, "Enter test timeout in seconds (default: 120): ")?;
handle.flush()?;
let stdin = io::stdin();
let mut line = String::new();
stdin.lock().read_line(&mut line)?;
let trimmed = line.trim();
if trimmed.is_empty() {
return Ok(120);
}
let timeout = trimmed.parse::<u32>().map_err(|_| {
CliError::InvalidInput(format!("'{}' is not a valid positive integer", trimmed))
})?;
if timeout == 0 {
return Err(CliError::InvalidInput(
"Timeout must be a positive integer (greater than 0)".to_string(),
));
}
Ok(timeout)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cli_parsing() {
let cli = Cli::parse_from(["rust-bucket", "apply"]);
match cli.command {
Commands::Apply { force } => assert!(!force),
}
let cli = Cli::parse_from(["rust-bucket", "apply", "--force"]);
match cli.command {
Commands::Apply { force } => assert!(force),
}
}
#[test]
fn test_version_flag() {
let result = Cli::try_parse_from(["rust-bucket", "--version"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.kind(), clap::error::ErrorKind::DisplayVersion);
}
}