happy_cracking/crypto/
numbersys.rs1use anyhow::{Context, Result};
2use clap::Subcommand;
3
4#[derive(Subcommand)]
5pub enum NumberSysAction {
6 #[command(about = "Convert number between bases (2-36)")]
7 Convert {
8 #[arg(help = "Input number as string")]
9 input: String,
10 #[arg(long, default_value_t = 10, help = "Source base (2-36)")]
11 from: u32,
12 #[arg(long, default_value_t = 16, help = "Target base (2-36)")]
13 to: u32,
14 },
15}
16
17pub fn run(action: NumberSysAction) -> Result<()> {
18 match action {
19 NumberSysAction::Convert { input, from, to } => {
20 println!("{}", convert(&input, from, to)?);
21 }
22 }
23 Ok(())
24}
25
26pub fn convert(input: &str, from_base: u32, to_base: u32) -> Result<String> {
27 let input = input.trim();
28
29 if !(2..=36).contains(&from_base) {
30 anyhow::bail!("Source base must be between 2 and 36, got {}", from_base);
31 }
32 if !(2..=36).contains(&to_base) {
33 anyhow::bail!("Target base must be between 2 and 36, got {}", to_base);
34 }
35 if input.is_empty() {
36 anyhow::bail!("Input cannot be empty");
37 }
38
39 let value = u128::from_str_radix(input, from_base).context("Failed to parse input number")?;
40
41 if value == 0 {
42 return Ok("0".to_string());
43 }
44
45 let mut result = Vec::new();
46 let mut v = value;
47
48 while v > 0 {
49 let digit = (v % to_base as u128) as u8;
50 let c = if digit < 10 {
51 b'0' + digit
52 } else {
53 b'a' + digit - 10
54 };
55 result.push(c as char);
56 v /= to_base as u128;
57 }
58
59 result.reverse();
60 Ok(result.into_iter().collect())
61}