use crate::EncodingTarget;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EncodingError {
InputIsTooLong,
ContainsNonLatinLetter,
}
pub trait EnumeratedLatinEncode<T: EncodingTarget> {
fn enumerated_latin_encode(&self) -> Result<T, EncodingError>;
}
impl<T: EncodingTarget> EnumeratedLatinEncode<T> for str {
fn enumerated_latin_encode(&self) -> Result<T, EncodingError> {
self.as_bytes().enumerated_latin_encode()
}
}
impl<T: EncodingTarget> EnumeratedLatinEncode<T> for String {
fn enumerated_latin_encode(&self) -> Result<T, EncodingError> {
self.as_bytes().enumerated_latin_encode()
}
}
impl<T: EncodingTarget> EnumeratedLatinEncode<T> for [u8] {
fn enumerated_latin_encode(&self) -> Result<T, EncodingError> {
if self.len() > T::MAX_ENUMERATED_LATIN_LEN {
return Err(EncodingError::InputIsTooLong);
}
let mut acc: T = 1.into(); for c in self {
let n: T = ascii_letter_to_number(*c)
.ok_or(EncodingError::ContainsNonLatinLetter)?
.into();
acc = (acc * 26.into()) + n;
}
Ok(acc)
}
}
fn ascii_letter_to_number(to_encode: u8) -> Option<u8> {
match to_encode {
b'a'..=b'z' => Some(to_encode - b'a'),
b'A'..=b'Z' => Some(to_encode - b'A'),
_ => None,
}
}
#[cfg(test)]
mod test {
use super::*;
use std::fmt::Debug;
#[test]
fn encode_test() {
assert_eq!("a".enumerated_latin_encode(), Ok(26 as i128));
assert_eq!("z".enumerated_latin_encode(), Ok(51 as i128));
assert_eq!("aa".enumerated_latin_encode(), Ok(676 as i128));
assert_eq!("zz".enumerated_latin_encode(), Ok(1351 as i128));
assert_eq!("Blubber".enumerated_latin_encode(), Ok(8480578981 as u64));
}
#[test]
fn encode_with_invalid_input() {
assert_eq!(
"blubber!".enumerated_latin_encode(),
Result::<u64, _>::Err(EncodingError::ContainsNonLatinLetter)
);
assert_eq!(
"hello world".enumerated_latin_encode(),
Result::<u64, _>::Err(EncodingError::ContainsNonLatinLetter)
);
}
fn expect_too_long<T: EncodingTarget + Debug>(text: &str) {
assert_eq!(
text.enumerated_latin_encode(),
Result::<T, _>::Err(EncodingError::InputIsTooLong),
);
}
#[test]
fn encode_too_long() {
expect_too_long::<u8>("aa");
expect_too_long::<i16>("aaa");
expect_too_long::<u16>("aaaa");
expect_too_long::<i32>("aaaaabb");
expect_too_long::<u32>("aaaaabb");
expect_too_long::<i64>("aaaaabbbbbcccc");
expect_too_long::<u64>("aaaaabbbbbcccc");
expect_too_long::<i128>("aaaaabbbbbcccccdddddeeeeeff");
expect_too_long::<u128>("aaaaabbbbbcccccdddddeeeeefff");
}
fn test_at_max_len<T: EncodingTarget + Debug + PartialEq>(text: &str, outcome: T) {
if text.len() != T::MAX_ENUMERATED_LATIN_LEN {
panic!(
"Test text isn't at expected maximum length of {}",
T::MAX_ENUMERATED_LATIN_LEN
);
}
assert_eq!(text.enumerated_latin_encode(), Ok(outcome));
}
#[test]
fn encode_at_max_len() {
test_at_max_len("z", 51 as u8);
test_at_max_len("zz", 1351 as i16);
test_at_max_len("zzz", 35151 as u16);
test_at_max_len("zzzzzz", 617831551 as i32);
test_at_max_len("zzzzzz", 617831551 as u32);
test_at_max_len("zzzzzzzzzzzzz", 4962305746407473151 as i64);
test_at_max_len("zzzzzzzzzzzzz", 4962305746407473151 as u64);
test_at_max_len(
"zzzzzzzzzzzzzzzzzzzzzzzzzz",
12312239160414314621593348576800407551 as i128,
);
test_at_max_len(
"zzzzzzzzzzzzzzzzzzzzzzzzzzz",
320118218170772180161427062996810596351 as u128,
);
}
#[test]
fn encode_empty() {
assert_eq!("".enumerated_latin_encode(), Ok(1 as u8))
}
}