1use alloc::string::String;
4
5#[must_use]
16pub fn rot13(s: &str) -> String {
17 s.chars()
18 .map(|c| match c {
19 'A'..='Z' => rotate(c, b'A'),
20 'a'..='z' => rotate(c, b'a'),
21 other => other,
22 })
23 .collect()
24}
25
26fn rotate(c: char, base: u8) -> char {
31 let offset = (c as u8).wrapping_sub(base);
32 char::from(base + (offset + 13) % 26)
33}
34
35#[cfg(test)]
36mod tests {
37 use super::rot13;
38
39 #[test]
40 fn rotates_both_cases() {
41 assert_eq!(rot13("Hello, World!"), "Uryyb, Jbeyq!");
42 }
43
44 #[test]
45 fn is_its_own_inverse() {
46 let plain = "The quick brown fox jumps over the lazy dog.";
47 assert_eq!(rot13(&rot13(plain)), plain);
48 }
49
50 #[test]
51 fn wraps_at_the_alphabet_boundary() {
52 assert_eq!(rot13("ABMNYZ"), "NOZALM");
54 assert_eq!(rot13("abmnyz"), "nozalm");
55 }
56
57 #[test]
58 fn leaves_non_letters_alone() {
59 assert_eq!(rot13("0123456789"), "0123456789");
60 assert_eq!(rot13(" \t\n{}[]|\\/@#"), " \t\n{}[]|\\/@#");
61 }
62
63 #[test]
64 fn leaves_non_ascii_alone() {
65 assert_eq!(rot13("café 日本語 🦀"), "pnsé 日本語 🦀");
67 }
68
69 #[test]
70 fn empty_input_yields_empty_output() {
71 assert_eq!(rot13(""), "");
72 }
73
74 #[test]
75 fn decodes_a_real_userassist_value_name() {
76 assert_eq!(
78 rot13(r"{P:\Jvaqbjf\flfgrz32\pzq.rkr"),
79 r"{C:\Windows\system32\cmd.exe"
80 );
81 }
82}