use crate::{symbol_code_from_bytes, ParseSymbolCodeError};
use core::{fmt, num::ParseIntError};
#[derive(Debug, PartialEq, Clone)]
pub enum ParseSymbolError {
Precision(ParseIntError),
CodeTooLong,
BadFormat,
BadChar(u8),
}
impl fmt::Display for ParseSymbolError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Precision(e) => {
write!(f, "symbol precision couldn't be parsed: {}", e)
}
Self::CodeTooLong => {
write!(f, "symbol code is too long, must be 7 chars or less")
}
Self::BadFormat => {
write!(f, "symbol is not in the correct format, should be similar to: 4,EOS")
}
Self::BadChar(c) => {
write!(f, "symbol contains invalid character '{}'", char::from(*c))
}
}
}
}
impl From<ParseSymbolCodeError> for ParseSymbolError {
fn from(err: ParseSymbolCodeError) -> Self {
match err {
ParseSymbolCodeError::TooLong => Self::CodeTooLong,
ParseSymbolCodeError::BadChar(c) => Self::BadChar(c),
}
}
}
#[inline]
pub fn symbol_from_bytes<I>(
precision: u8,
iter: I,
) -> Result<u64, ParseSymbolError>
where
I: Iterator<Item = u8>,
{
let code = symbol_code_from_bytes(iter)?;
Ok(symbol_from_code(precision, code))
}
#[inline]
#[must_use]
pub const fn symbol_from_code(precision: u8, code: u64) -> u64 {
(code << 8) | (precision as u64)
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub const fn symbol_to_precision(value: u64) -> u8 {
(value & 0xFF) as u8
}
#[inline]
#[must_use]
pub const fn symbol_to_code(value: u64) -> u64 {
value >> 8
}
#[cfg(test)]
mod tests {
use crate::*;
use proptest::prelude::*;
#[test]
fn from_bytes_to_code_and_precision() {
proptest!(|(precision in 0_u8.., code in "[A-Z]{1,7}")| {
let expected_code = symbol_code_from_bytes(code.bytes()).unwrap();
let symbol = symbol_from_bytes(precision, code.bytes()).unwrap();
let result_precision = symbol_to_precision(symbol);
prop_assert_eq!(result_precision, precision);
let result_code = symbol_to_code(symbol);
prop_assert_eq!(result_code, expected_code);
});
}
}