use crate::error::Error;

pub fn hex_to_bytes(s: &str) -> crate::error::Result<Vec<u8>> {
  let bytes = s.as_bytes();
  if bytes.len() % 2 != 0 {
    return Err(Error::new(format!(
      "hex string must have even length, got {}",
      bytes.len()
    )));
  }

  let mut out = Vec::with_capacity(bytes.len() / 2);
  for chunk in bytes.chunks(2) {
    let hi = match hex_val(chunk[0]) {
      Some(hi) => hi,
      None => {
        return Err(Error::new(format!(
          "invalid hex char '{}'",
          chunk[0] as char
        )));
      }
    };
    let lo = match hex_val(chunk[1]) {
      Some(lo) => lo,
      None => {
        return Err(Error::new(format!(
          "invalid hex char '{}'",
          chunk[1] as char
        )));
      }
    };
    out.push((hi << 4) | lo);
  }
  Ok(out)
}

fn hex_val(b: u8) -> Option<u8> {
  match b {
    b'0'..=b'9' => Some(b - b'0'),
    b'a'..=b'f' => Some(b - b'a' + 10),
    b'A'..=b'F' => Some(b - b'A' + 10),
    _ => None,
  }
}
