use super::{base32::args, Cmd}; use clap::{ArgMatches, Command}; use data_encoding::BASE64; use std::{ error::Error, fs, io::{self, Read, Write}, }; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; #[derive(Debug)] pub struct Base64; impl Cmd for Base64 { fn cli(&self) -> Command { Command::new("base64") .author("Nathan Fisher") .about("Base64 encode/decode data and print to standard output") .args(args()) } fn run(&self, matches: &ArgMatches) -> Result<(), Box> { let color = match matches.get_one::("color").map(String::as_str) { Some("always") => ColorChoice::Always, Some("ansi") => ColorChoice::AlwaysAnsi, Some("auto") => { if atty::is(atty::Stream::Stdout) { ColorChoice::Auto } else { ColorChoice::Never } } _ => ColorChoice::Never, }; if let Some(files) = matches.get_many::("file") { let (len, _) = files.size_hint(); for (index, file) in files.enumerate() { if { len > 1 || matches.get_flag("VERBOSE") } && !matches.get_flag("QUIET") { let mut stdout = StandardStream::stdout(color); stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; match index { 0 => writeln!(stdout, "===> {file} <==="), _ => writeln!(stdout, "\n===> {file} <==="), }?; stdout.reset()?; } else if index > 0 { println!(); } let contents = get_contents(file)?; if matches.get_flag("DECODE") { decode_base64(contents, matches.get_flag("IGNORE"))?; } else { encode_base64( &contents, match matches.get_one("WRAP") { Some(c) => *c, None => 76, }, ); } } } Ok(()) } fn path(&self) -> Option { Some(shitbox::Path::UsrBin) } } fn decode_base64(mut contents: String, ignore: bool) -> Result<(), Box> { if ignore { contents.retain(|c| !c.is_whitespace()); } else { contents = contents.replace('\n', ""); } let decoded = BASE64.decode(contents.as_bytes())?; let output = String::from_utf8(decoded)?; println!("{}\n", output.trim_end()); Ok(()) } fn encode_base64(contents: &str, wrap: usize) { BASE64 .encode(contents.as_bytes()) .chars() .collect::>() .chunks(wrap) .map(|c| c.iter().collect::()) .for_each(|line| println!("{line}")); } fn get_contents(file: &str) -> Result> { let mut contents = String::new(); if file == "-" { io::stdin().read_to_string(&mut contents)?; } else { contents = fs::read_to_string(file)?; } Ok(contents) }