dev_kit/command/
base64.rs

1use crate::command::StringInput;
2use base64::Engine;
3use itertools::Itertools;
4use std::fs;
5use std::path::PathBuf;
6use std::str::FromStr;
7
8#[derive(clap::Subcommand)]
9pub enum Base64Command {
10    #[clap(about = "base64 decode, alias 'd'", alias = "d")]
11    Decode {
12        #[arg(help = "base64 text to decode", default_value = "")]
13        input: StringInput,
14        #[arg(short, long, help = "url safe", default_value = "false")]
15        url_safe: bool,
16        #[arg(short, long, help = "no padding", default_value = "false")]
17        no_pad: bool,
18        #[arg(short, long, help = "raw output", default_value = "false")]
19        raw_output: bool,
20        #[arg(short, long, help = "file to write output")]
21        file: Option<PathBuf>,
22    },
23    #[clap(about = "base64 encode, alias 'e'", alias = "e")]
24    Encode {
25        #[arg(help = "base64 text to decode", default_value = "")]
26        input: StringInput,
27        #[arg(short, long, help = "url safe", default_value = "false")]
28        url_safe: bool,
29        #[arg(short, long, help = "no padding", default_value = "false")]
30        no_pad: bool,
31        #[arg(short, long, help = "file to write output")]
32        file: Option<PathBuf>,
33    },
34}
35
36pub fn decode(input: &str, url_safe: bool, no_pad: bool) -> crate::Result<Vec<u8>> {
37    let text = if let Some(input) = PathBuf::from_str(input).ok().and_then(|p| fs::read_to_string(p).ok()) {
38        input
39    } else {
40        input.to_string()
41    };
42    if url_safe {
43        if no_pad {
44            base64::prelude::BASE64_URL_SAFE.decode(text.as_bytes())
45        } else {
46            base64::prelude::BASE64_URL_SAFE_NO_PAD.decode(text.as_bytes())
47        }
48    } else {
49        if no_pad {
50            base64::prelude::BASE64_STANDARD_NO_PAD.decode(text.as_bytes())
51        } else {
52            base64::prelude::BASE64_STANDARD.decode(text.as_bytes())
53        }
54    }.map_err(|e| anyhow::anyhow!("base64 encode failed: {}", e))
55}
56
57pub fn encode(input: &str, url_safe: bool, no_pad: bool) -> crate::Result<String> {
58    let data = if let Some(input) = PathBuf::from_str(input).ok().and_then(|p| fs::read(p).ok()) {
59        input
60    } else {
61        input.as_bytes().to_vec()
62    };
63    let string = if url_safe {
64        if no_pad {
65            base64::prelude::BASE64_URL_SAFE.encode(data)
66        } else {
67            base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(data)
68        }
69    } else {
70        if no_pad {
71            base64::prelude::BASE64_STANDARD_NO_PAD.encode(data)
72        } else {
73            base64::prelude::BASE64_STANDARD.encode(data)
74        }
75    };
76    Ok(string)
77}
78
79pub enum Base64Val {
80    EncodeString(String),
81    DecodeBytes(Vec<u8>),
82}
83
84impl super::Command for Base64Command {
85    fn run(&self) -> crate::Result<()> {
86        match self {
87            Self::Decode { input, url_safe, no_pad, raw_output, file } => {
88                let data = decode(&input, *url_safe, *no_pad)?;
89                match (*raw_output, file) {
90                    (false, None) => {
91                        let text = String::from_utf8_lossy(&data);
92                        println!("{}", text);
93                    }
94                    (true, None) => {
95                        let text = data.chunks(8).flatten().map(|it| {
96                            format!("{:02x} ", *it)
97                        }).join("\n");
98                        println!("{}", text);
99                    }
100                    (false, Some(file)) => {
101                        let text = String::from_utf8_lossy(&data).to_string();
102                        let _ = std::fs::write(file, text)?;
103                        println!("write to {}", file.display())
104                    }
105                    (true, Some(file)) => {
106                        let _ = std::fs::write(file, data)?;
107                        println!("write to {}", file.display())
108                    }
109                }
110            }
111            Self::Encode { input, url_safe, no_pad, file } => {
112                let text = encode(&input, *url_safe, *no_pad)?;
113                if let Some(file) = file {
114                    let _ = std::fs::write(file, text)?;
115                    println!("write to {}", file.display())
116                } else {
117                    println!("{}", text);
118                }
119            }
120        }
121        Ok(())
122    }
123}
124
125