ari 0.0.6

ari. the extras library.
use std::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};

pub fn to_hex(data: &[u8]) -> String {
    format!("{:x}", HexSlice(data))
}

pub fn from_hex(string: &str) -> Result<Vec<u8>, std::io::Error> {
    let mut data = Vec::with_capacity(string.len() / 2);
    let mut value = 0;
    let mut processed = 0;

    for byte in string.bytes() {
        value <<= 4;

        #[rustfmt::skip]
        match byte {
            b'A'..=b'F' => value |= byte - b'A' + 10,
            b'a'..=b'f' => value |= byte - b'a' + 10,
            b'0'..=b'9' => value |= byte - b'0',

            b' '  => { value >>= 4; continue; },
            b'\r' => { value >>= 4; continue; },
            b'\n' => { value >>= 4; continue; },
            b'\t' => { value >>= 4; continue; },
            _ => return Err(std::io::ErrorKind::InvalidInput.into()),
        }

        processed += 1;

        if processed == 2 {
            data.push(value);
            processed = 0;
        }
    }

    match processed {
        0 => Ok(data.into_iter().collect()),
        _ => Err(std::io::ErrorKind::InvalidInput.into()),
    }
}

pub struct HexSlice<'a>(pub &'a [u8]);

impl Debug for HexSlice<'_> {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> {
        LowerHex::fmt(self, formatter)
    }
}

impl Display for HexSlice<'_> {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> {
        LowerHex::fmt(self, formatter)
    }
}

impl LowerHex for HexSlice<'_> {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> {
        for byte in self.0 {
            write!(formatter, "{:02x}", byte)?;
        }

        Ok(())
    }
}

impl UpperHex for HexSlice<'_> {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> {
        for byte in self.0 {
            write!(formatter, "{:02X}", byte)?;
        }

        Ok(())
    }
}