use clap::{Parser, Subcommand};
use rune_hex::{decode, encode, encode_upper};
use std::{
io::{self, Read, Write},
process::ExitCode,
};
#[derive(Parser)]
#[command(name = "rune-hex", version, author)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Encode {
file: Option<String>,
#[arg(short, long)]
upper: bool,
},
Decode {
input: Option<String>,
},
}
fn main() -> ExitCode {
let cli = Cli::parse();
match cli.command {
Command::Encode { file, upper } => {
let data = match read_bytes(file.as_deref()) {
Ok(d) => d,
Err(e) => {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
};
let hex = if upper { encode_upper(&data) } else { encode(&data) };
println!("{hex}");
}
Command::Decode { input } => {
let text = match read_text(input.as_deref()) {
Ok(t) => t,
Err(e) => {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
};
match decode(text.trim()) {
Ok(bytes) => {
if let Err(e) = io::stdout().write_all(&bytes) {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
}
Err(e) => {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
}
}
}
ExitCode::SUCCESS
}
fn read_bytes(path: Option<&str>) -> io::Result<Vec<u8>> {
match path {
None | Some("-") => {
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf)?;
Ok(buf)
}
Some(p) => std::fs::read(p),
}
}
fn read_text(path: Option<&str>) -> io::Result<String> {
match path {
None | Some("-") => {
let mut buf = String::new();
io::stdin().read_to_string(&mut buf)?;
Ok(buf)
}
Some(s) => Ok(s.to_owned()),
}
}