diff --git a/src/format.rs b/src/format.rs index fe425df..f756b1b 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,7 +1,11 @@ +#![allow(dead_code)] // this is exported to lib.rs +use clap::{ArgGroup, Parser}; +use clap_num::maybe_hex; use libpt::bintols::split; pub type Num = u128; +/// formats supported by numf #[derive(Copy, Clone, Debug)] pub enum Format { Dec, @@ -12,6 +16,140 @@ pub enum Format { Base32, } +/// Describes what the formatter should do +/// +/// Use [Self::default] to get a basic variant or create a object yourself. +/// +/// This struct can be parsed with [clap] derive. +#[derive(Parser, Debug, Clone, PartialEq, Eq, Hash)] +#[clap(author, version, about, long_about = None)] +#[command( + author, + version, + about, + long_about, + help_template = r#"{about-section} +{usage-heading} {usage} +{all-args}{tab} + +{name}: {version} +Author: {author-with-newline} +"# +)] +#[clap(group( + ArgGroup::new("format") + .args(&["hex", "bin", "oct", "dec", "base64", "base32"]), + ))] +pub struct FormatOptions { + #[arg(short, long)] + /// add a prefix (like "0x" for hex) + prefix: bool, + #[arg(short = 'P', long)] + /// add a padding to make the number at least one byte long + /// + /// For example, `0b1100` will be `0b00001100` with this. + /// This does not apply to all formats, only hexadecimal and binary. + padding: bool, + #[arg(short = 'x', long, default_value_t = true)] + /// format to hexadecimal + hex: bool, + #[arg(short, long)] + /// format to binary + bin: bool, + #[arg(short, long)] + /// format to decimal + dec: bool, + #[arg(short, long)] + /// format to octal + oct: bool, + #[arg(short = 's', long)] + /// format to base64 + base64: bool, + #[arg(short = 'z', long)] + /// format to base32 + base32: bool, + #[clap(value_parser=maybe_hex::, required=true)] + /// at least one number that should be formatted + /// + /// supports either base 10 or base 16 inputs (with 0xaaaa) + numbers: Vec, +} + +impl FormatOptions { + /// get the format that the user has configured + pub fn format(&self) -> Format { + if self.oct { + Format::Octal + } else if self.bin { + Format::Bin + } else if self.dec { + Format::Dec + } else if self.base64 { + Format::Base64 + } else if self.base32 { + Format::Base32 + } else if self.hex { + Format::Hex + } else { + unreachable!() + } + } + + /// set the format manually + pub fn set_format(&mut self, format: Format) { + self.bin = false; + self.oct = false; + self.dec = false; + self.hex = false; + self.base64 = false; + self.base32 = false; + match format { + Format::Bin => self.bin = true, + Format::Hex => self.hex = true, + Format::Octal => self.oct = true, + Format::Base64 => self.base64 = true, + Format::Base32 => self.base32 = true, + Format::Dec => self.dec = true, + } + } + + /// get numbers + pub fn numbers(&self) -> &[u128] { + self.numbers.as_ref() + } + + /// set numbers manually + pub fn set_numbers(&mut self, numbers: Vec) { + self.numbers = numbers; + } + + /// get padding + pub fn padding(&self) -> bool { + self.padding + } + + /// get prefix + pub fn prefix(&self) -> bool { + self.prefix + } +} + +impl Default for FormatOptions { + fn default() -> Self { + Self { + padding: false, + prefix: false, + oct: false, + hex: true, + bin: false, + base32: false, + base64: false, + dec: false, + numbers: vec![], + } + } +} + impl Format { pub fn prefix(&self) -> String { match self { @@ -31,17 +169,27 @@ impl Format { } .to_string() } - pub fn format(&self, num: Num, prefix: bool) -> String { + pub fn format(&self, num: Num, options: &FormatOptions) -> String { let mut buf = String::new(); - if prefix { + if options.prefix { buf += &self.prefix(); } match self { Format::Hex => { - buf += &format!("{num:X}"); + if options.padding { + let tmp = &format!("{num:X}"); + buf += &("0".repeat((2 - tmp.len() % 2) % 2) + tmp); + } else { + buf += &format!("{num:X}"); + } } Format::Bin => { - buf += &format!("{num:b}"); + if options.padding { + let tmp = &format!("{num:b}"); + buf += &("0".repeat((8 - tmp.len() % 8) % 8) + tmp); + } else { + buf += &format!("{num:b}"); + } } Format::Octal => { buf += &format!("{num:o}"); diff --git a/src/main.rs b/src/main.rs index 927ddfb..1aa0470 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,87 +3,18 @@ //! This binary should just take any amount of numbers and print them out formatted to some other //! system. -use clap::{ArgGroup, Parser}; -use clap_num::maybe_hex; +use clap::Parser; mod format; use format::*; -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -#[command( - author, - version, - about, - long_about, - help_template = r#"{about-section} -{usage-heading} {usage} -{all-args}{tab} - -{name}: {version} -Author: {author-with-newline} -"# -)] -#[clap(group( - ArgGroup::new("format") - .args(&["hex", "bin", "oct", "dec", "base64", "base32"]), - ))] -struct Cli { - #[arg(short, long)] - /// add a prefix (like "0x" for hex) - prefix: bool, - #[arg(short = 'x', long, default_value_t = true)] - /// format to hexadecimal - hex: bool, - #[arg(short, long)] - /// format to binary - bin: bool, - #[arg(short, long)] - /// format to decimal - dec: bool, - #[arg(short, long)] - /// format to octal - oct: bool, - #[arg(short = 's', long)] - /// format to base64 - base64: bool, - #[arg(short = 'z', long)] - /// format to base32 - base32: bool, - #[clap(value_parser=maybe_hex::, required=true)] - /// at least one number that should be formatted - /// - /// supports either base 10 or base 16 inputs (with 0xaaaa) - numbers: Vec, -} - -impl Cli { - fn format(&self) -> Format { - if self.oct { - Format::Octal - } else if self.bin { - Format::Bin - } else if self.dec { - Format::Dec - } else if self.base64 { - Format::Base64 - } else if self.base32 { - Format::Base32 - } else if self.hex { - Format::Hex - } else { - unreachable!() - } - } -} - fn main() { - let cli = Cli::parse(); + let options = FormatOptions::parse(); let mut out: Vec = Vec::new(); - for num in &cli.numbers { - out.push(cli.format().format(*num, cli.prefix)); + for num in options.numbers() { + out.push(options.format().format(*num, &options)); } for o in out { println!("{o}")