use std::fs;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::process::ExitCode;
use clap::Parser;
use scissors::{
approve_file_in_place, approve_in_editor, FileOutcome, Options, Outcome, ScissorsError,
};
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[arg(value_name = "FILE")]
file: Option<PathBuf>,
#[arg(long, value_name = "TEXT")]
context: Option<String>,
#[arg(long)]
yes: bool,
}
fn main() -> ExitCode {
let cli = Cli::parse();
let opts = match cli.context.as_deref() {
Some(ctx) => Options::new().context(ctx),
None => Options::new(),
};
if let Some(path) = cli.file.as_deref() {
if path != Path::new("-") {
if cli.yes {
return match fs::read_to_string(path) {
Ok(c) if !c.trim().is_empty() => ExitCode::SUCCESS,
Ok(_) => {
eprintln!("scissors: {} is empty; nothing to approve", path.display());
ExitCode::from(1)
}
Err(e) => {
eprintln!("scissors: cannot read {}: {e}", path.display());
ExitCode::from(2)
}
};
}
return match approve_file_in_place(path, &opts) {
Ok(FileOutcome::Approved) => ExitCode::SUCCESS,
Ok(FileOutcome::Aborted) => {
eprintln!("scissors: aborted; {} left unchanged", path.display());
ExitCode::from(1)
}
Err(e) => {
eprintln!("scissors: {e}");
ExitCode::from(2)
}
};
}
}
let mut content = String::new();
if let Err(e) = io::stdin().read_to_string(&mut content) {
eprintln!("scissors: failed to read stdin: {e}");
return ExitCode::from(2);
}
if cli.yes {
println!("{}", content.trim_end());
return ExitCode::SUCCESS;
}
match approve_in_editor(&content, &opts) {
Ok(Outcome::Approved(approved)) => {
println!("{approved}");
io::stdout().flush().ok();
ExitCode::SUCCESS
}
Ok(Outcome::Aborted { draft_path }) => {
eprintln!(
"scissors: aborted; draft preserved at {}",
draft_path.display()
);
ExitCode::from(1)
}
Err(err) => {
eprintln!("scissors: {err}");
if matches!(err, ScissorsError::NoEditor) {
eprintln!(
"scissors: hint: in a non-interactive environment, pass --yes \
to approve without editing"
);
}
ExitCode::from(2)
}
}
}