happy-cracking 0.3.0

A fast, comprehensive CTF toolkit for cryptographic encoding/decoding, classic ciphers, hash operations, and analysis tools
Documentation
use anyhow::Result;
use clap::Subcommand;

#[derive(Subcommand)]
pub enum StrToolsAction {
    #[command(about = "Reverse a string")]
    Reverse {
        #[arg(help = "Input text")]
        input: String,
    },
    #[command(about = "Show ASCII/Unicode code points for each character")]
    Ord {
        #[arg(help = "Input text")]
        input: String,
    },
    #[command(about = "Convert space-separated code points to characters")]
    Chr {
        #[arg(help = "Space-separated code points (e.g. \"72 101 108 108 111\")")]
        input: String,
    },
}

pub fn run(action: StrToolsAction) -> Result<()> {
    match action {
        StrToolsAction::Reverse { input } => {
            println!("{}", reverse(&input));
        }
        StrToolsAction::Ord { input } => {
            println!("{}", ord(&input));
        }
        StrToolsAction::Chr { input } => {
            println!("{}", chr(&input)?);
        }
    }
    Ok(())
}

// Reverse a string by Unicode grapheme.
pub fn reverse(input: &str) -> String {
    input.chars().rev().collect()
}

// Return each character with its code point in "A=65 B=66" format.
pub fn ord(input: &str) -> String {
    if input.is_empty() {
        return String::new();
    }

    // Optimization: avoid format! and collect overhead by pre-allocating
    // and manually formatting the numbers into a buffer.
    // Each entry is max 1 char + 1 '=' + 7 digits (max u32 for char is 1114111) + 1 space = 10 chars.
    let mut out = String::with_capacity(input.len() * 10);
    let mut first = true;

    for c in input.chars() {
        if !first {
            out.push(' ');
        }
        first = false;

        out.push(c);
        out.push('=');

        let mut n = c as u32;
        if n == 0 {
            out.push('0');
        } else {
            let mut buf = [0u8; 10];
            let mut i = 10;
            while n > 0 {
                i -= 1;
                buf[i] = (n % 10) as u8 + b'0';
                n /= 10;
            }
            // SAFETY: buf only contains ascii digits b'0'..=b'9'
            out.push_str(unsafe { std::str::from_utf8_unchecked(&buf[i..]) });
        }
    }

    out
}

// Convert space-separated numeric values to a string.
pub fn chr(input: &str) -> Result<String> {
    if input.is_empty() {
        return Ok(String::new());
    }

    input
        .split_whitespace()
        .map(|s| {
            let n: u32 = s
                .parse()
                .map_err(|_| anyhow::anyhow!("Invalid code point: {}", s))?;
            char::from_u32(n).ok_or_else(|| anyhow::anyhow!("Invalid Unicode code point: {}", n))
        })
        .collect()
}