Skip to main content

happy_cracking/crypto/
strtools.rs

1use anyhow::Result;
2use clap::Subcommand;
3use umt_rust::string::umt_reverse_string;
4
5#[derive(Subcommand)]
6pub enum StrToolsAction {
7    #[command(about = "Reverse a string")]
8    Reverse {
9        #[arg(help = "Input text")]
10        input: String,
11    },
12    #[command(about = "Show ASCII/Unicode code points for each character")]
13    Ord {
14        #[arg(help = "Input text")]
15        input: String,
16    },
17    #[command(about = "Convert space-separated code points to characters")]
18    Chr {
19        #[arg(help = "Space-separated code points (e.g. \"72 101 108 108 111\")")]
20        input: String,
21    },
22}
23
24pub fn run(action: StrToolsAction) -> Result<()> {
25    match action {
26        StrToolsAction::Reverse { input } => {
27            println!("{}", reverse(&input));
28        }
29        StrToolsAction::Ord { input } => {
30            println!("{}", ord(&input));
31        }
32        StrToolsAction::Chr { input } => {
33            println!("{}", chr(&input)?);
34        }
35    }
36    Ok(())
37}
38
39// Reverse a string by Unicode grapheme.
40pub fn reverse(input: &str) -> String {
41    umt_reverse_string(input)
42}
43
44// Return each character with its code point in "A=65 B=66" format.
45pub fn ord(input: &str) -> String {
46    if input.is_empty() {
47        return String::new();
48    }
49
50    // Optimization: avoid format! and collect overhead by pre-allocating
51    // and manually formatting the numbers into a buffer.
52    // Each entry is max 1 char + 1 '=' + 7 digits (max u32 for char is 1114111) + 1 space = 10 chars.
53    let mut out = String::with_capacity(input.len() * 10);
54    let mut first = true;
55
56    for c in input.chars() {
57        if !first {
58            out.push(' ');
59        }
60        first = false;
61
62        out.push(c);
63        out.push('=');
64
65        let mut n = c as u32;
66        if n == 0 {
67            out.push('0');
68        } else {
69            let mut buf = [0u8; 10];
70            let mut i = 10;
71            while n > 0 {
72                i -= 1;
73                buf[i] = (n % 10) as u8 + b'0';
74                n /= 10;
75            }
76            // SAFETY: buf only contains ascii digits b'0'..=b'9'
77            out.push_str(unsafe { std::str::from_utf8_unchecked(&buf[i..]) });
78        }
79    }
80
81    out
82}
83
84// Convert space-separated numeric values to a string.
85pub fn chr(input: &str) -> Result<String> {
86    if input.is_empty() {
87        return Ok(String::new());
88    }
89
90    input
91        .split_whitespace()
92        .map(|s| {
93            let n: u32 = s
94                .parse()
95                .map_err(|_| anyhow::anyhow!("Invalid code point: {}", s))?;
96            char::from_u32(n).ok_or_else(|| anyhow::anyhow!("Invalid Unicode code point: {}", n))
97        })
98        .collect()
99}