pub fn atbash(text: &str) -> String {
String::from_utf8(atbash_bytes(text.as_bytes()))
.expect("atbash preserves UTF-8 validity: only ASCII bytes are modified")
}
pub fn atbash_bytes(bytes: &[u8]) -> Vec<u8> {
bytes.iter().copied().map(mirror_byte).collect()
}
fn mirror_byte(byte: u8) -> u8 {
match byte {
b'a'..=b'z' => b'a' + b'z' - byte,
b'A'..=b'Z' => b'A' + b'Z' - byte,
_ => byte,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn atbash_empty() {
assert_eq!(atbash(""), "");
}
#[test]
fn atbash_lowercase_full_alphabet() {
assert_eq!(
atbash("abcdefghijklmnopqrstuvwxyz"),
"zyxwvutsrqponmlkjihgfedcba"
);
}
#[test]
fn atbash_uppercase_full_alphabet() {
assert_eq!(
atbash("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
"ZYXWVUTSRQPONMLKJIHGFEDCBA"
);
}
#[test]
fn atbash_known_vector() {
assert_eq!(atbash("Hello, World!"), "Svool, Dliow!");
}
#[test]
fn atbash_case_preserved() {
assert_eq!(atbash("aAbBzZ"), "zZyYaA");
}
#[test]
fn atbash_non_letters_unchanged() {
assert_eq!(atbash("123 !@# \t\n"), "123 !@# \t\n");
}
#[test]
fn atbash_idempotent() {
let inputs = ["Hello, World!", "The quick brown fox", "abc XYZ 123 !"];
for input in inputs {
assert_eq!(atbash(&atbash(input)), input);
}
}
#[test]
fn atbash_bytes_empty() {
assert_eq!(atbash_bytes(b""), b"");
}
#[test]
fn atbash_bytes_non_ascii_unchanged() {
assert_eq!(atbash_bytes(b"\x00\x80\xff"), b"\x00\x80\xff");
}
#[test]
fn atbash_bytes_idempotent() {
let original = b"Attack at dawn! \x00\xff";
assert_eq!(atbash_bytes(&atbash_bytes(original)), original);
}
#[test]
fn atbash_roundtrip() {
let plaintext = "The quick brown fox jumps over the lazy dog.";
assert_eq!(atbash(&atbash(plaintext)), plaintext);
}
#[test]
fn atbash_boundary_letters() {
assert_eq!(atbash("azAZ"), "zaZA");
}
}