1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use base64_stream::{FromBase64Reader, ToBase64Reader};
use clap::Parser;
use rustutils_runnable::Runnable;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

/// Base64 encode or decode a file, or standard input, to standard output.
#[derive(Parser, Clone, Debug)]
#[clap(author, version, about, long_about = None)]
pub struct Base64 {
    /// Wrap encoded lines after COLS characters.
    #[clap(long, short, default_value = "76")]
    wrap: u16,
    /// Don't perform any wrapping.
    #[clap(long, short, conflicts_with = "wrap")]
    no_wrap: bool,
    /// Decode data rather than encoding.
    #[clap(long, short)]
    decode: bool,
    /// When decoding, ignore non-alphabet characters.
    #[clap(long, short, requires = "decode")]
    ignore_garbage: bool,
    /// File to encode or decode.
    ///
    /// When omitted, standard input is read instead.
    file: Option<PathBuf>,
}

impl Base64 {
    pub fn wrapping(&self) -> Option<u16> {
        if self.no_wrap || self.wrap == 0 {
            None
        } else {
            Some(self.wrap)
        }
    }

    pub fn run(&self) -> Result<(), Box<dyn Error>> {
        let input: Box<dyn Read> = match &self.file {
            Some(file) if file.as_os_str() != "-" => Box::new(File::open(file)?),
            _ => Box::new(std::io::stdin()),
        };
        match self.decode {
            false => self.encode(input)?,
            true => self.decode(input)?,
        }
        Ok(())
    }

    pub fn encode(&self, stream: Box<dyn Read>) -> Result<(), Box<dyn Error>> {
        let mut encoder = ToBase64Reader::new(stream);
        let mut stdout = std::io::stdout();
        std::io::copy(&mut encoder, &mut stdout)?;
        Ok(())
    }

    pub fn decode(&self, stream: Box<dyn Read>) -> Result<(), Box<dyn Error>> {
        let mut encoder = FromBase64Reader::new(stream);
        let mut stdout = std::io::stdout();
        std::io::copy(&mut encoder, &mut stdout)?;
        Ok(())
    }
}

impl Runnable for Base64 {
    fn run(&self) -> Result<(), Box<dyn Error>> {
        self.run()?;
        Ok(())
    }
}