Merge pull request 'feat: padding and FormatOptions #9' (#11) from feat/padding into master
cargo devel CI / cargo CI (push) Successful in 1m14s Details

Reviewed-on: #11
This commit is contained in:
Christoph J. Scherr 2024-05-12 19:59:19 +02:00
commit 6251546a31
2 changed files with 156 additions and 77 deletions

View File

@ -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; use libpt::bintols::split;
pub type Num = u128; pub type Num = u128;
/// formats supported by numf
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Format { pub enum Format {
Dec, Dec,
@ -12,6 +16,140 @@ pub enum Format {
Base32, 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::<Num>, required=true)]
/// at least one number that should be formatted
///
/// supports either base 10 or base 16 inputs (with 0xaaaa)
numbers: Vec<Num>,
}
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<Num>) {
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 { impl Format {
pub fn prefix(&self) -> String { pub fn prefix(&self) -> String {
match self { match self {
@ -31,17 +169,27 @@ impl Format {
} }
.to_string() .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(); let mut buf = String::new();
if prefix { if options.prefix {
buf += &self.prefix(); buf += &self.prefix();
} }
match self { match self {
Format::Hex => { 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 => { 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 => { Format::Octal => {
buf += &format!("{num:o}"); buf += &format!("{num:o}");

View File

@ -3,87 +3,18 @@
//! This binary should just take any amount of numbers and print them out formatted to some other //! This binary should just take any amount of numbers and print them out formatted to some other
//! system. //! system.
use clap::{ArgGroup, Parser}; use clap::Parser;
use clap_num::maybe_hex;
mod format; mod format;
use 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::<Num>, required=true)]
/// at least one number that should be formatted
///
/// supports either base 10 or base 16 inputs (with 0xaaaa)
numbers: Vec<Num>,
}
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() { fn main() {
let cli = Cli::parse(); let options = FormatOptions::parse();
let mut out: Vec<String> = Vec::new(); let mut out: Vec<String> = Vec::new();
for num in &cli.numbers { for num in options.numbers() {
out.push(cli.format().format(*num, cli.prefix)); out.push(options.format().format(*num, &options));
} }
for o in out { for o in out {
println!("{o}") println!("{o}")