feat: add a parser that can be used with clap for all formats except base32 and base64
cargo devel CI / cargo CI (push) Failing after 1m16s Details

Refs: #5
This commit is contained in:
Christoph J. Scherr 2024-05-13 13:19:49 +02:00
parent b007a54b4b
commit 399021ecc6
2 changed files with 103 additions and 18 deletions

View File

@ -1,9 +1,10 @@
#![allow(dead_code)] // this is exported to lib.rs
#![allow(dead_code)]
use anyhow::anyhow;
// this is exported to lib.rs
use clap::{ArgGroup, Parser};
use clap_num::maybe_hex;
use libpt::bintols::split;
pub type Num = u128;
pub type NumberType = u128;
/// formats supported by numf
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
@ -68,11 +69,11 @@ pub struct FormatOptions {
#[arg(short = 'z', long)]
/// format to base32
base32: bool,
#[clap(value_parser=maybe_hex::<Num>, required=true)]
#[clap(value_parser=numf_parser, required=true)]
/// at least one number that should be formatted
///
/// supports either base 10 or base 16 inputs (with 0xaaaa)
numbers: Vec<Num>,
numbers: Vec<NumberType>,
}
impl FormatOptions {
@ -119,7 +120,7 @@ impl FormatOptions {
}
/// set numbers manually
pub fn set_numbers(&mut self, numbers: Vec<Num>) {
pub fn set_numbers(&mut self, numbers: Vec<NumberType>) {
self.numbers = numbers;
}
@ -161,6 +162,7 @@ impl Default for FormatOptions {
}
impl Format {
/// Get the perfix for that [Format]
pub fn prefix(&self) -> String {
match self {
// apperently used nowhere, sometimes 0 is used as a prefix but I
@ -179,14 +181,15 @@ impl Format {
}
.to_string()
}
pub fn format(&self, num: Num, options: &FormatOptions) -> String {
/// format a number with a [Format] and [FormatOptions]
pub fn format(&self, num: NumberType, options: &FormatOptions) -> String {
let mut buf = String::new();
if options.prefix {
if options.prefix() {
buf += &self.prefix();
}
match self {
Format::Hex => {
if options.padding {
if options.padding() {
let tmp = &format!("{num:X}");
buf += &("0".repeat((2 - tmp.len() % 2) % 2) + tmp);
} else {
@ -194,22 +197,104 @@ impl Format {
}
}
Format::Bin => {
if options.padding {
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}");
}
Format::Dec => {
buf += &format!("{num}");
}
Format::Octal => buf += &format!("{num:o}"),
Format::Dec => buf += &format!("{num}"),
Format::Base64 => buf += &fast32::base64::RFC4648.encode(&split::unsigned_to_vec(num)),
Format::Base32 => buf += &fast32::base32::RFC4648.encode(&split::unsigned_to_vec(num)),
}
buf
}
}
/// Validates an unsigned integer value that can be one of [Format](format::Format).
///
/// The number is assumed to be base-10 by default, it is parsed as a different
/// [Format](format::Format) if the number is prefixed with the [prefix](format::FormatOptions::prefix),
/// case insensitive. So if the user inputs `0b1100` then this is parsed as
/// [Binary](format::Format::Bin) and so on.
///
/// # Example
///
/// This allows base-10 addresses to be passed normally, or values formatted with any of the
/// [Formats](format::Format) defined by this crate to be passed when prefixed with the respective
/// prefix.
///
/// ```
/// use clap::Parser;
/// use numf::parser::numf_parser;
///
/// #[derive(Parser)]
/// struct Args {
/// #[clap(short, long, value_parser=numf_parser::<u32>)]
/// address: u32,
/// }
/// let args = Args::parse_from(&["", "-a", "0x10"]);
/// assert_eq!(args.address, 16);
/// ```
pub fn numf_parser(s: &str) -> anyhow::Result<NumberType> {
if s.starts_with(&Format::Dec.prefix()) || s.parse::<NumberType>().is_ok() {
let s = match s.strip_prefix(&Format::Dec.prefix()) {
Some(sr) => sr,
None => s,
};
match s.parse() {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if s.starts_with(&Format::Hex.prefix()) {
let s = match s.strip_prefix(&Format::Hex.prefix()) {
Some(sr) => sr,
None => s,
};
match NumberType::from_str_radix(s, 16) {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if s.starts_with(&Format::Octal.prefix()) {
let s = match s.strip_prefix(&Format::Octal.prefix()) {
Some(sr) => sr,
None => s,
};
match NumberType::from_str_radix(s, 8) {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if s.starts_with(&Format::Bin.prefix()) {
let s = match s.strip_prefix(&Format::Bin.prefix()) {
Some(sr) => sr,
None => s,
};
match NumberType::from_str_radix(s, 2) {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if s.starts_with(&Format::Base64.prefix()) {
let e = "parsing of base64 is not yet implemented".to_string();
Err(anyhow!(e))
} else if s.starts_with(&Format::Base32.prefix()) {
let e = "parsing of base32 is not yet implemented".to_string();
Err(anyhow!(e))
} else {
let e = "could not determine the format of the value".to_string();
Err(anyhow!(e))
}
}

View File

@ -3,8 +3,8 @@
//! This crate contains several utility functions for formatting numbers
//! into other systems, such as converting decimal numbers to hexadecimal.
//!
//! See [Format] for supported formats.
//! See [format::Format] for supported formats.
//!
//! Note that this crate is primarily used as a executable.
//! Note that this crate is primarily intended to be used as a executable.
pub mod format;