use proptest::prelude::*;
use proptest::test_runner::TestCaseError;
pub(crate) fn check_cut(text: &str, kept: &str, units: usize) -> Result<(), TestCaseError> {
prop_assert!(
text.starts_with(kept),
"{:?} is not a prefix of {:?}",
kept,
text
);
let kept_units = kept.encode_utf16().count();
prop_assert!(kept_units <= units, "{:?} is over {} units", kept, units);
if let Some(next) = text[kept.len()..].chars().next() {
prop_assert!(
kept_units + next.len_utf16() > units,
"{:?} could keep {:?} within {} units",
kept,
next,
units
);
}
Ok(())
}
pub(crate) fn base62_bytes(text: &str) -> [u8; 20] {
let mut bytes = [0_u8; 20];
for c in text.chars() {
let digit = match c {
'0'..='9' => u32::from(c) - u32::from('0'),
'A'..='Z' => u32::from(c) - u32::from('A') + 10,
'a'..='z' => u32::from(c) - u32::from('a') + 36,
_ => panic!("{c:?} is not a base62 digit"),
};
let mut carry = digit;
for byte in bytes.iter_mut().rev() {
let value = u32::from(*byte) * 62 + carry;
*byte = u8::try_from(value & 0xff).unwrap();
carry = value >> 8;
}
assert_eq!(carry, 0, "{text} is more than 20 bytes");
}
bytes
}
mod tests {
use proptest::test_runner::TestCaseError;
use super::{base62_bytes, check_cut};
#[test]
fn check_cut_takes_only_the_longest_prefix_within_the_units() {
let ok = |text: &str, kept: &str, units: usize| check_cut(text, kept, units).is_ok();
assert!(ok("abc", "ab", 2));
assert!(ok("abc", "abc", 5));
assert!(ok("a😀b", "a😀", 3));
assert!(ok("a😀b", "a", 2));
assert!(!ok("abc", "a", 2), "a shorter prefix than the units allow");
assert!(!ok("abc", "abc", 2), "over the units");
assert!(!ok("abc", "bc", 2), "not a prefix");
let error: Result<(), TestCaseError> = check_cut("abc", "x", 1);
assert!(error.is_err());
}
#[test]
fn base62_bytes_reads_the_smallest_and_the_largest_ksuid() {
assert_eq!(base62_bytes("000000000000000000000000000"), [0; 20]);
assert_eq!(base62_bytes("aWgEPTl1tmebfsQzFP4bxwgy80V"), [0xff; 20]);
assert_eq!(base62_bytes("000000000000000000000000001")[19], 1);
assert_eq!(base62_bytes("00000000000000000000000000z")[19], 61);
}
}