use rand::{rngs::OsRng, seq::SliceRandom}; use std::collections::HashSet; use std::env; use std::process; static CHARACTER_SETS: [(&str, &str, &str); 10] = [ ("lowercase", "abcdefghijklmnopqrstuvwxyz", "English lowercase letters (a-z)"), ("uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "English uppercase letters (A-Z)"), ("numbers", "0123456789", "Numbers (0-9)"), ("special", "!@#$%^&*()_+-=[]{}|;:,.<>?", "Special characters"), ("special-safe", "!@#$%^&*_+-=<>?", "Safe special chars (no pipes/brackets)"), ("cyrillic-lower", "абвгдежзийклмнопрстуфхцчшщъыьэюя", "Cyrillic lowercase"), ("cyrillic-upper", "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ", "Cyrillic uppercase"), ("greek-lower", "αβγδεζηθικλμνξοπρστυφχψω", "Greek lowercase"), ("greek-upper", "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ", "Greek uppercase"), ("symbols", "©®™€£¥§¶†‡•…‰′″‹›\"\'–—", "Extended symbols"), ]; fn print_help() { println!("rs-random: Secure String Generator\n"); println!("Usage:"); println!(" rs-random [LENGTH] (uses safe defaults)"); println!(" rs-random -l [-s ] [-c ]"); println!(" rs-random -h\n"); println!("Available sets (for -s, comma-separated):"); for &(name, _, desc) in &CHARACTER_SETS { println!(" {:<15} - {}", name, desc); } } fn get_chars(selected_sets: &[&str]) -> Result, String> { let mut all_chars = Vec::new(); let mut unknown = Vec::new(); for &set_name in selected_sets { match CHARACTER_SETS.iter().find(|&&(name, _, _)| name == set_name) { Some(&(_, chars, _)) => all_chars.extend(chars.chars()), None => unknown.push(set_name), } } if !unknown.is_empty() { return Err(format!("Unknown sets: {}", unknown.join(","))); } if all_chars.is_empty() { return Err("Empty character pool".to_string()); } // Remove duplicates let mut unique_chars = Vec::new(); let mut seen = HashSet::new(); for ch in all_chars { if seen.insert(ch) { unique_chars.push(ch); } } Ok(unique_chars) } fn generate_random_string(chars: &[char], length: usize) -> String { let mut rng = OsRng; (0..length) .map(|_| *chars.choose(&mut rng).unwrap()) .collect() } fn main() { let args: Vec = env::args().collect(); if args.len() < 2 || args.contains(&"-h".to_string()) { print_help(); return; } let mut length = 16; let mut count = 1; let mut sets = "lowercase,uppercase,numbers,special-safe"; let mut i = 1; while i < args.len() { match args[i].as_str() { "-l" => { if i + 1 < args.len() { if let Ok(n) = args[i + 1].parse::() { length = n; } else { eprintln!("Error: Invalid length"); process::exit(1); } i += 2; } else { eprintln!("Error: Missing length value"); process::exit(1); } } "-s" => { if i + 1 < args.len() { sets = &args[i + 1]; i += 2; } else { eprintln!("Error: Missing sets value"); process::exit(1); } } "-c" => { if i + 1 < args.len() { if let Ok(n) = args[i + 1].parse::() { count = n; } else { eprintln!("Error: Invalid count"); process::exit(1); } i += 2; } else { eprintln!("Error: Missing count value"); process::exit(1); } } arg if i == 1 && !arg.starts_with("-") => { if let Ok(n) = arg.parse::() { length = n; } else { eprintln!("Error: Invalid length"); process::exit(1); } i += 1; } _ => { i += 1; } } } if length == 0 { eprintln!("Error: Length must be > 0"); process::exit(1); } let selected_sets: Vec<&str> = sets.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()).collect(); match get_chars(&selected_sets) { Ok(chars) => { for i in 0..count { let random_string = generate_random_string(&chars, length); if count > 1 { print!("{}: ", i + 1); } println!("{}", random_string); } } Err(e) => { eprintln!("Error: {}", e); process::exit(1); } } }