use std::fs::File; use std::io::{self, Read}; use std::path::PathBuf; use std::process; use base64::{alphabet, engine, read, write}; use clap::Parser; #[derive(Clone, Debug, Parser, strum::EnumString, Default)] #[strum(serialize_all = "kebab-case")] enum Alphabet { #[default] Standard, UrlSafe, } /// Base64 encode or decode FILE (or standard input), to standard output. #[derive(Debug, Parser)] struct Opt { /// Decode the base64-encoded input (default: encode the input as base64). #[structopt(short = 'd', long = "decode")] decode: bool, /// The encoding alphabet: "standard" (default) or "url-safe". #[structopt(long = "alphabet")] alphabet: Option, /// Omit padding characters while encoding, and reject them while decoding. #[structopt(short = 'p', long = "no-padding")] no_padding: bool, /// The file to encode or decode. #[structopt(name = "FILE", parse(from_os_str))] file: Option, } fn main() { let opt = Opt::parse(); let stdin; let mut input: Box = match opt.file { None => { stdin = io::stdin(); Box::new(stdin.lock()) } Some(ref f) if f.as_os_str() == "-" => { stdin = io::stdin(); Box::new(stdin.lock()) } Some(f) => Box::new(File::open(f).unwrap()), }; let alphabet = opt.alphabet.unwrap_or_default(); let engine = engine::GeneralPurpose::new( &match alphabet { Alphabet::Standard => alphabet::STANDARD, Alphabet::UrlSafe => alphabet::URL_SAFE, }, match opt.no_padding { true => engine::general_purpose::NO_PAD, false => engine::general_purpose::PAD, }, ); let stdout = io::stdout(); let mut stdout = stdout.lock(); let r = if opt.decode { let mut decoder = read::DecoderReader::new(&mut input, &engine); io::copy(&mut decoder, &mut stdout) } else { let mut encoder = write::EncoderWriter::new(&mut stdout, &engine); io::copy(&mut input, &mut encoder) }; if let Err(e) = r { eprintln!( "Base64 {} failed with {}", if opt.decode { "decode" } else { "encode" }, e ); process::exit(1); } }