1 use std::fs::File;
2 use std::io::{self, Read};
3 use std::path::PathBuf;
4 use std::process;
5 
6 use base64::{alphabet, engine, read, write};
7 use clap::Parser;
8 
9 #[derive(Clone, Debug, Parser, strum::EnumString, Default)]
10 #[strum(serialize_all = "kebab-case")]
11 enum Alphabet {
12     #[default]
13     Standard,
14     UrlSafe,
15 }
16 
17 /// Base64 encode or decode FILE (or standard input), to standard output.
18 #[derive(Debug, Parser)]
19 struct Opt {
20     /// Decode the base64-encoded input (default: encode the input as base64).
21     #[structopt(short = 'd', long = "decode")]
22     decode: bool,
23 
24     /// The encoding alphabet: "standard" (default) or "url-safe".
25     #[structopt(long = "alphabet")]
26     alphabet: Option<Alphabet>,
27 
28     /// Omit padding characters while encoding, and reject them while decoding.
29     #[structopt(short = 'p', long = "no-padding")]
30     no_padding: bool,
31 
32     /// The file to encode or decode.
33     #[structopt(name = "FILE", parse(from_os_str))]
34     file: Option<PathBuf>,
35 }
36 
main()37 fn main() {
38     let opt = Opt::parse();
39     let stdin;
40     let mut input: Box<dyn Read> = match opt.file {
41         None => {
42             stdin = io::stdin();
43             Box::new(stdin.lock())
44         }
45         Some(ref f) if f.as_os_str() == "-" => {
46             stdin = io::stdin();
47             Box::new(stdin.lock())
48         }
49         Some(f) => Box::new(File::open(f).unwrap()),
50     };
51 
52     let alphabet = opt.alphabet.unwrap_or_default();
53     let engine = engine::GeneralPurpose::new(
54         &match alphabet {
55             Alphabet::Standard => alphabet::STANDARD,
56             Alphabet::UrlSafe => alphabet::URL_SAFE,
57         },
58         match opt.no_padding {
59             true => engine::general_purpose::NO_PAD,
60             false => engine::general_purpose::PAD,
61         },
62     );
63 
64     let stdout = io::stdout();
65     let mut stdout = stdout.lock();
66     let r = if opt.decode {
67         let mut decoder = read::DecoderReader::new(&mut input, &engine);
68         io::copy(&mut decoder, &mut stdout)
69     } else {
70         let mut encoder = write::EncoderWriter::new(&mut stdout, &engine);
71         io::copy(&mut input, &mut encoder)
72     };
73     if let Err(e) = r {
74         eprintln!(
75             "Base64 {} failed with {}",
76             if opt.decode { "decode" } else { "encode" },
77             e
78         );
79         process::exit(1);
80     }
81 }
82