use core::fmt;
pub const SYMBOL_CODE_CHARS: [u8; 26] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
pub const SYMBOL_CODE_MAX_LEN: usize = 7;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ParseSymbolCodeError {
TooLong,
BadChar(u8),
}
impl fmt::Display for ParseSymbolCodeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::TooLong => {
write!(f, "symbol is too long, must be 7 chars or less")
}
Self::BadChar(c) => write!(
f,
"symbol contains invalid character '{}'",
char::from(c)
),
}
}
}
#[inline]
pub fn symbol_code_from_bytes<I>(iter: I) -> Result<u64, ParseSymbolCodeError>
where
I: Iterator<Item = u8>,
{
let mut value = 0_u64;
for (i, c) in iter.enumerate() {
if i == SYMBOL_CODE_MAX_LEN {
return Err(ParseSymbolCodeError::TooLong);
} else if c < b'A' || c > b'Z' {
return Err(ParseSymbolCodeError::BadChar(c));
} else {
value <<= 8;
value |= u64::from(c);
}
}
Ok(value)
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub fn symbol_code_to_bytes(value: u64) -> [u8; SYMBOL_CODE_MAX_LEN] {
let mut chars = [b' '; SYMBOL_CODE_MAX_LEN];
let mut v = value;
for c in chars.iter_mut().rev() {
if v == 0 {
break;
}
*c = (v & 0xFF) as u8;
v >>= 8;
}
chars
}
#[cfg(test)]
mod tests {
use super::*;
use core::str;
use proptest::prelude::*;
#[test]
fn from_bytes_to_bytes() {
proptest!(|(input in "[A-Z]{0,7}")| {
let value = symbol_code_from_bytes(input.bytes()).unwrap();
let bytes = symbol_code_to_bytes(value);
let string = str::from_utf8(&bytes).unwrap();
prop_assert_eq!(string, format!("{:>7}", input));
});
}
#[test]
fn from_bytes_too_long() {
proptest!(|(input in "[A-Z]{8}")| {
if symbol_code_from_bytes(input.bytes()).is_ok() {
panic!("Should've gotten TooLong error with input '{}'", input);
};
});
}
#[test]
fn to_bytes_doesnt_crash() {
proptest!(|(input in 0_u64..)| {
let _ = symbol_code_to_bytes(input);
});
}
}