use wafrift_types::hash::{FNV_PRIME_64, fnv1a_64};
pub fn alternating_case(payload: &str, start_upper: bool) -> String {
let mut upper = start_upper;
let mut out = String::with_capacity(payload.len());
for ch in payload.chars() {
if ch.is_ascii_alphabetic() {
out.push(if upper {
ch.to_ascii_uppercase()
} else {
ch.to_ascii_lowercase()
});
upper = !upper;
} else {
out.push(ch);
}
}
out
}
pub fn case_alternate(payload: &str) -> String {
alternating_case(payload, true)
}
pub fn random_case_alternate(payload: &str) -> String {
let seed: u64 = fnv1a_64(payload.as_bytes());
let mut out = String::with_capacity(payload.len());
for (i, ch) in payload.chars().enumerate() {
if ch.is_ascii_alphabetic() {
let mixed = seed.wrapping_add(i as u64).wrapping_mul(FNV_PRIME_64);
out.push(if mixed & 1 == 0 {
ch.to_ascii_uppercase()
} else {
ch.to_ascii_lowercase()
});
} else {
out.push(ch);
}
}
out
}
pub fn lowercase(payload: &str) -> String {
payload.to_ascii_lowercase()
}
pub fn uppercase(payload: &str) -> String {
payload.to_ascii_uppercase()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn case_alternate_basic() {
assert_eq!(case_alternate("select"), "SeLeCt");
}
#[test]
fn random_case_preserves_content() {
let a = random_case_alternate("SELECT");
assert_eq!(a.to_ascii_lowercase(), "select");
}
#[test]
fn lowercase_basic() {
assert_eq!(lowercase("SeLeCt"), "select");
}
#[test]
fn uppercase_basic() {
assert_eq!(uppercase("SeLeCt"), "SELECT");
}
#[test]
fn case_alternate_idempotent_after_first() {
let once = case_alternate("select");
let twice = case_alternate(&once);
assert_eq!(
once, twice,
"case_alternate must be idempotent after first application"
);
assert_eq!(once, "SeLeCt");
}
#[test]
fn random_case_is_deterministic() {
let a = random_case_alternate("SELECT");
let b = random_case_alternate("SELECT");
assert_eq!(a, b, "random_case_alternate must be deterministic");
let c = random_case_alternate("SELECTS");
assert_ne!(
a, c,
"different input must produce different output (not just a fixed-case encoder)"
);
}
#[test]
fn random_case_mixes_both_cases() {
let out = random_case_alternate("SELECT");
let has_lower = out.chars().any(|c| c.is_ascii_lowercase());
let has_upper = out.chars().any(|c| c.is_ascii_uppercase());
assert!(
has_lower && has_upper,
"random_case_alternate must mix both cases for 'SELECT', got: {out}"
);
}
}