use data_encoding::{self, BASE32, BASE64};
use std::io::{self, Read, Write};
use data_encoding::{Encoding, BASE32HEX, BASE64URL, HEXUPPER};
use data_encoding_macro::new_encoding;
#[cfg(feature = "thiserror")]
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DecodeError {
#[error("{}", _0)]
Decode(#[from] data_encoding::DecodeError),
#[error("{}", _0)]
DecodeZ85(#[from] z85::DecodeError),
#[error("{}", _0)]
Io(#[from] io::Error),
}
pub enum EncodeError {
Z85InputLenNotMultipleOf4,
}
pub type DecodeResult = Result<Vec<u8>, DecodeError>;
#[derive(Clone, Copy)]
pub enum Format {
Base64,
Base64Url,
Base32,
Base32Hex,
Base16,
Base2Lsbf,
Base2Msbf,
Z85,
}
use self::Format::*;
const BASE2LSBF: Encoding = new_encoding! {
symbols: "01",
bit_order: LeastSignificantFirst,
};
const BASE2MSBF: Encoding = new_encoding! {
symbols: "01",
bit_order: MostSignificantFirst,
};
pub fn encode(f: Format, input: &[u8]) -> Result<String, EncodeError> {
Ok(match f {
Base32 => BASE32.encode(input),
Base64 => BASE64.encode(input),
Base64Url => BASE64URL.encode(input),
Base32Hex => BASE32HEX.encode(input),
Base16 => HEXUPPER.encode(input),
Base2Lsbf => BASE2LSBF.encode(input),
Base2Msbf => BASE2MSBF.encode(input),
Z85 => {
if input.len() % 4 == 0 {
z85::encode(input)
} else {
return Err(EncodeError::Z85InputLenNotMultipleOf4);
}
}
})
}
pub fn decode(f: Format, input: &[u8]) -> DecodeResult {
Ok(match f {
Base32 => BASE32.decode(input)?,
Base64 => BASE64.decode(input)?,
Base64Url => BASE64URL.decode(input)?,
Base32Hex => BASE32HEX.decode(input)?,
Base16 => HEXUPPER.decode(input)?,
Base2Lsbf => BASE2LSBF.decode(input)?,
Base2Msbf => BASE2MSBF.decode(input)?,
Z85 => {
if input.starts_with(&[b'#']) {
return Err(z85::DecodeError::InvalidByte(0, b'#').into());
} else {
z85::decode(input)?
}
}
})
}
pub struct Data<R: Read> {
line_wrap: usize,
ignore_garbage: bool,
input: R,
format: Format,
alphabet: &'static [u8],
}
impl<R: Read> Data<R> {
pub fn new(input: R, format: Format) -> Self {
Self {
line_wrap: 76,
ignore_garbage: false,
input,
format,
alphabet: match format {
Base32 => b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
Base64 => b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=+/",
Base64Url => b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=_-",
Base32Hex => b"0123456789ABCDEFGHIJKLMNOPQRSTUV=",
Base16 => b"0123456789ABCDEF",
Base2Lsbf => b"01",
Base2Msbf => b"01",
Z85 => b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#",
},
}
}
#[must_use]
pub fn line_wrap(mut self, wrap: usize) -> Self {
self.line_wrap = wrap;
self
}
#[must_use]
pub fn ignore_garbage(mut self, ignore: bool) -> Self {
self.ignore_garbage = ignore;
self
}
pub fn decode(&mut self) -> DecodeResult {
let mut buf = vec![];
self.input.read_to_end(&mut buf)?;
if self.ignore_garbage {
buf.retain(|c| self.alphabet.contains(c));
} else {
buf.retain(|&c| c != b'\r' && c != b'\n');
};
decode(self.format, &buf)
}
pub fn encode(&mut self) -> Result<String, EncodeError> {
let mut buf: Vec<u8> = vec![];
self.input.read_to_end(&mut buf).unwrap();
encode(self.format, buf.as_slice())
}
}
pub fn wrap_print<R: Read>(data: &Data<R>, res: &str) {
let stdout = io::stdout();
wrap_write(stdout.lock(), data.line_wrap, res).unwrap();
}
pub fn wrap_write<W: Write>(mut writer: W, line_wrap: usize, res: &str) -> io::Result<()> {
use std::cmp::min;
if line_wrap == 0 {
return write!(writer, "{res}");
}
let mut start = 0;
while start < res.len() {
let end = min(start + line_wrap, res.len());
writeln!(writer, "{}", &res[start..end])?;
start = end;
}
Ok(())
}