Skip to main content

happy_cracking/crypto/
numbersys.rs

1use 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}