use std::{error, fmt, io, str};
use std::io::{Read, Write};
use base64::engine::{DecodeEstimate, Engine};
use base64::engine::general_purpose::{
GeneralPurpose, GeneralPurposeConfig, STANDARD
};
pub use base64::{DecodeError, DecodeSliceError};
pub struct Xml;
impl Xml {
const ENGINE: GeneralPurpose = STANDARD;
pub fn decode(self, input: &str) -> Result<Vec<u8>, XmlDecodeError> {
let mut res = Vec::with_capacity(
Self::ENGINE.internal_decoded_len_estimate(
input.len()
).decoded_len_estimate()
);
base64::read::DecoderReader::new(
SkipWhitespace::new(input),
&Self::ENGINE,
).read_to_end(&mut res)?;
Ok(res)
}
pub fn decode_bytes(
self, input: &[u8]
) -> Result<Vec<u8>, XmlDecodeError> {
let input = str::from_utf8(input).map_err(|err| {
let pos = err.valid_up_to();
io::Error::other(
DecodeError::InvalidByte(pos, input[pos])
)
})?;
self.decode(input)
}
pub fn encode(self, data: &[u8]) -> String {
Self::ENGINE.encode(data)
}
pub fn decode_reader(
self, input: &str,
) -> XmlDecoderReader<'_> {
XmlDecoderReader(
base64::read::DecoderReader::new(
SkipWhitespace::new(input),
&Self::ENGINE
)
)
}
pub fn encode_writer(
self, writer: impl io::Write
) -> impl io::Write {
base64::write::EncoderWriter::new(writer, &Self::ENGINE)
}
}
pub struct Serde;
impl Serde {
const ENGINE: GeneralPurpose = STANDARD;
pub fn decode(self, input: &str) -> Result<Vec<u8>, DecodeError> {
Self::ENGINE.decode(input)
}
pub fn encode(self, data: &[u8]) -> String {
Self::ENGINE.encode(data)
}
}
pub struct Slurm;
impl Slurm {
const ENGINE: GeneralPurpose = GeneralPurpose::new(
&base64::alphabet::URL_SAFE,
GeneralPurposeConfig::new()
.with_encode_padding(false)
.with_decode_padding_mode(
base64::engine::DecodePaddingMode::Indifferent
)
);
pub fn decode(self, input: &str) -> Result<Vec<u8>, DecodeError> {
Self::ENGINE.decode(input)
}
pub fn decode_slice(
self, input: &str, output: &mut [u8]
) -> Result<usize, DecodeSliceError> {
Self::ENGINE.decode_slice(input, output)
}
pub fn encode(self, data: &[u8]) -> String {
Self::ENGINE.encode(data)
}
pub fn write_encoded_slice(
self, data: &[u8], target: &mut impl io::Write
) -> Result<(), io::Error> {
let mut enc = base64::write::EncoderWriter::new(target, &Self::ENGINE);
enc.write_all(data)?;
enc.finish()?;
Ok(())
}
pub fn display(self, data: &[u8]) -> impl fmt::Display + '_ {
base64::display::Base64Display::new(data, &Self::ENGINE)
}
}
struct SkipWhitespace<'a> {
splitter: str::SplitAsciiWhitespace<'a>,
current: &'a [u8],
}
impl<'a> SkipWhitespace<'a> {
fn new(s: &'a str) -> Self {
Self {
splitter: s.split_ascii_whitespace(),
current: b"",
}
}
}
impl io::Read for SkipWhitespace<'_> {
fn read(&mut self, mut buf: &mut[u8]) -> Result<usize, io::Error> {
let mut res = 0;
loop {
if self.current.is_empty() {
let next = match self.splitter.next() {
Some(data) => data,
None => return Ok(res)
};
self.current = next.as_bytes();
}
let current_len = self.current.len();
let buf_len = buf.len();
if current_len < buf_len {
buf[..current_len].copy_from_slice(self.current);
res += current_len;
buf = &mut buf[current_len..];
self.current = b"";
}
else {
let (head, tail) = self.current.split_at(buf_len);
buf.copy_from_slice(head);
self.current = tail;
return Ok(res + buf_len);
}
}
}
}
pub struct XmlDecoderReader<'a>(
base64::read::DecoderReader<
'static, GeneralPurpose, SkipWhitespace<'a>
>
);
impl io::Read for XmlDecoderReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
self.0.read(buf)
}
}
#[derive(Debug)]
pub struct XmlDecodeError(io::Error);
impl From<io::Error> for XmlDecodeError {
fn from(err: io::Error) -> Self {
Self(err)
}
}
impl fmt::Display for XmlDecodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0.get_ref() {
Some(inner) => fmt::Display::fmt(inner, f),
None => fmt::Display::fmt(&self.0, f),
}
}
}
impl error::Error for XmlDecodeError { }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn skip_whitespace() {
fn test(s: &str) {
let mut left = String::from(s);
left.retain(|ch| !ch.is_ascii_whitespace());
let mut right = String::new();
SkipWhitespace::new(s).read_to_string(&mut right).unwrap();
assert_eq!(left, right);
let mut right = Vec::new();
let mut reader = SkipWhitespace::new(s);
loop {
let mut buf = [0u8; 2];
let read = reader.read(&mut buf).unwrap();
if read == 0 {
break
}
right.extend_from_slice(buf.as_ref());
}
assert_eq!(left.as_bytes(), right.as_slice());
}
test("foobar");
test("foo bar");
test(" foo bar");
test(" foo bar ");
}
}