use std::cmp::{Ord, PartialEq, PartialOrd};
use std::convert::From;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use crate::ParseError;
#[derive(Eq, Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Default)]
pub struct SymbolCode {
value: u64,
}
impl SymbolCode {
#[inline]
#[must_use]
pub fn raw(&self) -> u64 {
self.value
}
#[inline]
#[must_use]
pub fn length(&self) -> u32 {
let mut sym: u64 = self.value;
let mut len: u32 = 0;
while sym & 0xFF > 0 && len <= 7 {
len += 1;
sym >>= 8;
}
len
}
#[inline]
#[must_use]
pub fn is_valid(&self) -> bool {
let mut sym: u64 = self.value;
let mut i = 0;
while i < 7 {
let c = sym as u8 as char;
if !c.is_ascii_uppercase() {
return false;
}
sym >>= 8;
if sym == 0 {
while i < 7 {
sym >>= 8;
if (sym & 0xFF) != 0 {
return false;
}
i += 1;
}
}
i += 1;
}
true
}
#[inline]
#[must_use]
pub fn new() -> Self {
Self { value: 0 }
}
}
impl Display for SymbolCode {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mask = 0x00000000000000FF;
if self.value == 0 {
return Result::Ok(());
}
let mut begin = "".to_string();
let mut v = self.value;
let mut i = 0;
while i < 7 {
if v == 0 {
break;
}
let c = (v & mask) as u8 as char;
begin.push(c);
v >>= 8;
i += 1;
}
f.write_str(begin.as_str())
}
}
impl From<&str> for SymbolCode {
#[inline]
#[must_use]
fn from(str: &str) -> Self {
Self::from_str(str).unwrap_or_else(|e| panic!("{}", e))
}
}
impl FromStr for SymbolCode {
type Err = ParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut value: u64 = 0;
if s.len() > 7 {
return Err(ParseError::BadSymbolCode(s.to_string()));
}
for c in s.chars().rev() {
if !c.is_ascii_uppercase() {
return Err(ParseError::BadSymbolCode(s.to_string()));
}
value <<= 8;
value |= c as u64;
}
Ok(SymbolCode { value })
}
}
impl From<u64> for SymbolCode {
#[inline]
#[must_use]
fn from(value: u64) -> Self {
SymbolCode { value }
}
}
impl From<SymbolCode> for u64 {
#[inline]
#[must_use]
fn from(symcode: SymbolCode) -> Self {
symcode.value
}
}
impl AsRef<SymbolCode> for SymbolCode {
#[inline]
#[must_use]
fn as_ref(&self) -> &SymbolCode {
self
}
}
impl From<SymbolCode> for bool {
#[inline]
#[must_use]
fn from(symcode: SymbolCode) -> Self {
symcode.raw() != 0
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn test_cdt_1() {
assert_eq!(0, SymbolCode::new().raw());
assert_eq!(0 as u64, SymbolCode::new().into());
}
#[test]
fn test_cdt_2() {
assert_eq!(0, SymbolCode::from(0).raw());
assert_eq!(1, SymbolCode::from(1).raw());
assert_eq!(u64::MAX, SymbolCode::from(u64::MAX).raw());
}
#[test]
fn test_cdt_3() {
assert_eq!(65, SymbolCode::from("A").raw());
assert_eq!(90, SymbolCode::from("Z").raw());
assert_eq!(18367622009667905, SymbolCode::from("AAAAAAA").raw());
assert_eq!(25432092013386330, SymbolCode::from("ZZZZZZZ").raw());
}
#[test]
fn test_cdt_4() {
assert_eq!(true, SymbolCode::from(65).is_valid()); assert_eq!(true, SymbolCode::from(90).is_valid()); assert_eq!(true, SymbolCode::from(18367622009667905).is_valid()); assert_eq!(true, SymbolCode::from(25432092013386330).is_valid());
assert_eq!(false, SymbolCode::from(64).is_valid());
assert_eq!(false, SymbolCode::from(25432092013386331).is_valid());
}
#[test]
fn test_cdt_5() {
assert_eq!(0, SymbolCode::from("").length());
assert_eq!(1, SymbolCode::from("S").length());
assert_eq!(2, SymbolCode::from("SY").length());
assert_eq!(3, SymbolCode::from("SYM").length());
assert_eq!(4, SymbolCode::from("SYMB").length());
assert_eq!(5, SymbolCode::from("SYMBO").length());
assert_eq!(6, SymbolCode::from("SYMBOL").length());
assert_eq!(7, SymbolCode::from("SYMBOLL").length());
}
#[test]
fn test_cdt_6() {
assert_eq!(false, SymbolCode::from(0).into());
assert_eq!(true, SymbolCode::from(1).into());
assert_eq!(false, SymbolCode::from("").into());
assert_eq!(true, SymbolCode::from("SYMBOL").into());
}
#[test]
fn test_cdt_7() {
assert_eq!("A", SymbolCode::from("A").to_string());
assert_eq!("Z", SymbolCode::from("Z").to_string());
assert_eq!("AAAAAAA", SymbolCode::from("AAAAAAA").to_string());
assert_eq!("ZZZZZZZ", SymbolCode::from("ZZZZZZZ").to_string());
}
#[test]
fn test_cdt_8() {
assert_eq!(true, SymbolCode::from("A") == SymbolCode::from("A"));
assert_eq!(true, SymbolCode::from("Z") == SymbolCode::from("Z"));
assert_eq!(true, SymbolCode::from("AAAAAAA") == SymbolCode::from("AAAAAAA"));
assert_eq!(true, SymbolCode::from("ZZZZZZZ") == SymbolCode::from("ZZZZZZZ"));
}
#[test]
fn test_cdt_9() {
assert_eq!(true, SymbolCode::from("A") != SymbolCode::new());
assert_eq!(true, SymbolCode::from("Z") != SymbolCode::new());
assert_eq!(true, SymbolCode::from("AAAAAAA") != SymbolCode::new());
assert_eq!(true, SymbolCode::from("ZZZZZZZ") != SymbolCode::new());
}
#[test]
fn test_cdt_10() {
assert_eq!(true, SymbolCode::new() < SymbolCode::from("A"));
assert_eq!(true, SymbolCode::new() < SymbolCode::from("Z"));
assert_eq!(true, SymbolCode::new() < SymbolCode::from("AAAAAAA"));
assert_eq!(true, SymbolCode::new() < SymbolCode::from("ZZZZZZZ"));
}
#[test]
#[allow(unused)]
#[should_panic(expected = "bad symbol code: ABCDEFGH")]
fn test_cdt_panic_1() {
SymbolCode::from("ABCDEFGH");
}
#[test]
#[allow(unused)]
#[should_panic(expected = "bad symbol code: a")]
fn test_cdt_panic_2a() {
SymbolCode::from("a");
}
#[test]
#[allow(unused)]
#[should_panic(expected = "bad symbol code: @")]
fn test_cdt_panic_2b() {
SymbolCode::from("@");
}
#[test]
fn test_as_ref() {
assert_eq!(5197638, SymbolCode::from("FOO").as_ref().value);
assert_eq!(5390658, SymbolCode::from("BAR").as_ref().value);
}
#[test]
fn test_length() {
assert_eq!(1, SymbolCode::from("A").length());
assert_eq!(2, SymbolCode::from("AB").length());
assert_eq!(3, SymbolCode::from("ABC").length());
assert_eq!(4, SymbolCode::from("ABCD").length());
assert_eq!(5, SymbolCode::from("ABCDE").length());
assert_eq!(6, SymbolCode::from("ABCDEF").length());
assert_eq!(7, SymbolCode::from("ABCDEFG").length());
}
#[test]
fn test_partial_eq() {
assert_eq!(true, SymbolCode::from(5197638) == SymbolCode::from(5197638));
assert_eq!(true, SymbolCode::from(0) != SymbolCode::from(5197638));
assert_eq!(false, SymbolCode::from(0) == SymbolCode::from(5197638));
assert_eq!(true, SymbolCode::from(5197637) != SymbolCode::from(5197638));
}
#[test]
fn test_partial_cmp() {
assert_eq!(true, SymbolCode::from(0) < SymbolCode::from(1));
assert_eq!(false, SymbolCode::from(3) < SymbolCode::from(2));
}
#[test]
fn test_new() {
assert_eq!("FOO", SymbolCode::from("FOO").to_string());
assert_eq!(5197638, SymbolCode::from("FOO").raw());
}
#[test]
fn test_fmt() {
assert_eq!("FOO", format!("{}", SymbolCode::from("FOO")));
assert_eq!("", format!("{}", SymbolCode::new()));
}
#[test]
fn test_println() {
println!("{}", SymbolCode::from("FOO"));
}
#[test]
fn test_from() {
assert_eq!(0, SymbolCode::from(0).value);
assert_eq!(0, SymbolCode::from(0).raw());
assert_eq!(0, SymbolCode::from("").raw());
assert_eq!(5197638, SymbolCode::from("FOO").value);
assert_eq!(5197638, SymbolCode::from(5197638).raw());
}
#[test]
fn test_is_valid() {
assert_eq!(false, SymbolCode::new().is_valid());
assert_eq!(false, SymbolCode::from(0).is_valid());
assert_eq!(false, SymbolCode::from(std::u64::MAX).is_valid());
assert_eq!(true, SymbolCode::from(5197638).is_valid());
assert_eq!(true, SymbolCode::from("FOO").is_valid());
}
#[test]
fn test_to_string() {
assert_eq!("FOO", SymbolCode::from(5197638).to_string());
assert_eq!("FOO", SymbolCode::from("FOO").to_string());
assert_eq!("ABCDEFG", SymbolCode::from("ABCDEFG").to_string());
}
#[test]
fn test_to_str() {
assert_eq!("FOO", SymbolCode::from("FOO").to_string());
}
#[test]
#[allow(unused)]
#[should_panic(expected = "bad symbol code: ABCDEFGH")]
fn test_from_string_long_panic_1() {
SymbolCode::from("ABCDEFGH");
}
#[test]
#[allow(unused)]
#[should_panic(expected = "bad symbol code: abc")]
fn test_from_string_letters_panic_1() {
SymbolCode::from("abc");
}
#[test]
#[allow(unused)]
#[should_panic(expected = "bad symbol code: 123")]
fn test_from_string_letters_panic_2() {
SymbolCode::from("123");
}
#[test]
fn test_ord() {
let symbol_code1 = SymbolCode::from("ABCDEFG");
let symbol_code2 = SymbolCode::from("ABCDEFH");
let symbol_code3 = SymbolCode::from("ABCDEF");
assert!(symbol_code1 < symbol_code2);
assert!(symbol_code1 > symbol_code3);
assert!(symbol_code1 > symbol_code3);
assert!(symbol_code3 < symbol_code2);
assert!(symbol_code1 <= symbol_code1);
assert!(symbol_code1 >= symbol_code1);
}
#[test]
fn test_clone() {
let symbol_code = SymbolCode::from("ABCDEFG");
let cloned_symbol_code = symbol_code.clone();
assert_eq!(symbol_code, cloned_symbol_code);
assert_eq!(5197638, SymbolCode::from("FOO").clone().value);
}
#[test]
fn test_from_self() {
let symcode = SymbolCode::from("ABCDEFG");
assert_eq!(SymbolCode::from(symcode), symcode);
}
#[test]
fn test_from_str() {
assert_eq!(SymbolCode::from_str("ABCDEFG").unwrap().to_string(), "ABCDEFG");
assert_eq!("ABCD1F".parse::<SymbolCode>(), Err(ParseError::BadSymbolCode("ABCD1F".to_string())));
assert_eq!("a".parse::<SymbolCode>(), Err(ParseError::BadSymbolCode("a".to_string())));
assert_eq!("".parse::<SymbolCode>(), Ok(SymbolCode { value: 0 }));
assert_eq!(
"ABCDEFGH".parse::<SymbolCode>(),
Err(ParseError::BadSymbolCode("ABCDEFGH".to_string()))
);
}
#[test]
fn test_to_bool() {
assert_eq!(true, SymbolCode::from("ABCDEFG").into());
assert_eq!(false, SymbolCode::default().into());
assert_eq!(false, SymbolCode::from("").into());
}
proptest! {
#[test]
fn random_sym_codes(input in "[[A-Z]]{1,7}") {
let symcode = SymbolCode::from(input.as_str());
prop_assert_eq!(symcode.to_string(), input);
}
}
}