#![allow(dead_code)]
use std::fmt::Display;
use anyhow::anyhow;
use clap::{ArgGroup, Parser};
use libpt::bintols::{join, split};
use libpt::cli::args::VerbosityLevel;
use libpt::log::{debug, trace};
pub type NumberType = u128;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Default)]
pub enum Format {
Dec,
#[default]
Hex,
Bin,
Octal,
Base64,
Base32,
Raw,
}
impl Display for Format {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Parser, Debug, Clone, PartialEq, Eq, Hash)]
#[command(
author,
version,
about,
long_about,
help_template = libpt::cli::args::HELP_TEMPLATE)]
#[clap(group(
ArgGroup::new("format")
.args(&["hex", "bin", "oct", "dec", "base64", "base32", "raw"]),
))]
pub struct FormatOptions {
#[arg(short, long)]
prefix: bool,
#[arg(short = 'P', long)]
padding: bool,
#[arg(short = 'x', long)]
hex: bool,
#[arg(short, long)]
bin: bool,
#[arg(short, long)]
dec: bool,
#[arg(short, long)]
oct: bool,
#[arg(short = 's', long)]
base64: bool,
#[arg(short = 'a', long)]
raw: bool,
#[arg(short = 'r', long, default_value_t = 0, value_parser=numf_parser_str::<NumberType>)]
rand: NumberType,
#[arg(long, default_value_t = NumberType::MAX, value_parser=numf_parser_str::<NumberType>)]
rand_max: NumberType,
#[arg(short = 'z', long)]
base32: bool,
#[clap(value_parser=numf_parser_str::<NumberType>, required=false)]
numbers: Vec<NumberType>,
#[command(flatten)]
pub(crate) verbosity: VerbosityLevel,
}
impl FormatOptions {
pub fn format(&self) -> Format {
trace!("self.hex: {}", self.hex);
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 if self.raw {
Format::Raw
} else {
debug!("no mode was explicitly selected, going with the default");
Format::default()
}
}
pub fn set_format(&mut self, format: Format) {
self.bin = false;
self.oct = false;
self.dec = false;
self.hex = false;
self.base64 = false;
self.raw = false;
self.base32 = false;
match format {
Format::Bin => self.bin = true,
Format::Raw => self.raw = 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,
}
}
pub fn numbers(&self) -> &[u128] {
self.numbers.as_ref()
}
pub fn set_numbers(&mut self, numbers: Vec<NumberType>) {
self.numbers = numbers;
}
pub fn set_padding(&mut self, value: bool) {
self.padding = value
}
pub fn padding(&self) -> bool {
self.padding
}
pub fn prefix(&self) -> bool {
self.prefix
}
pub fn set_prefix(&mut self, value: bool) {
self.prefix = value;
}
pub fn push_number(&mut self, value: NumberType) {
self.numbers.push(value)
}
pub fn rand(&self) -> NumberType {
self.rand
}
pub fn set_rand(&mut self, rand: NumberType) {
self.rand = rand;
}
pub fn rand_max(&self) -> NumberType {
self.rand_max
}
pub fn set_rand_max(&mut self, rand_max: NumberType) {
self.rand_max = rand_max;
}
}
impl Default for FormatOptions {
fn default() -> Self {
Self {
padding: false,
prefix: false,
oct: false,
hex: false,
bin: false,
raw: false,
base32: false,
base64: false,
dec: false,
numbers: vec![],
rand: 0,
rand_max: NumberType::MAX,
verbosity: VerbosityLevel::default(),
}
}
}
impl Format {
pub fn prefix_str(&self) -> String {
String::from_utf8_lossy(&self.prefix()).to_string()
}
pub fn prefix(&self) -> Vec<u8> {
match self {
Format::Dec => b"0d".to_vec(),
Format::Raw => [0x00].to_vec(),
Format::Hex => b"0x".to_vec(),
Format::Bin => b"0b".to_vec(),
Format::Octal => b"0o".to_vec(),
Format::Base64 => b"0s".to_vec(),
Format::Base32 => b"032s".to_vec(),
}
}
pub fn format_str(&self, num: NumberType, options: &FormatOptions) -> String {
String::from_utf8_lossy(&self.format(num, options)).to_string()
}
pub fn format(&self, num: NumberType, options: &FormatOptions) -> Vec<u8> {
debug!("formatting mode: {self}");
let mut buf: Vec<u8> = Vec::new();
if options.prefix() {
buf.append(&mut self.prefix());
debug!("prefix the buffer: {buf:X?}");
}
match self {
Format::Hex => {
if options.padding() {
let tmp = &format!("{num:X}");
let tmp1 = &("0".repeat((2 - tmp.len() % 2) % 2) + tmp);
buf.append(&mut tmp1.as_bytes().to_owned());
} else {
buf.append(&mut format!("{num:X}").as_bytes().to_owned());
}
}
Format::Bin => {
if options.padding() {
let tmp = &format!("{num:b}");
let tmp1 = &("0".repeat((8 - tmp.len() % 8) % 8) + tmp);
buf.append(&mut tmp1.as_bytes().to_owned());
} else {
buf.append(&mut format!("{num:b}").as_bytes().to_owned());
}
}
Format::Octal => buf.append(&mut format!("{num:o}").as_bytes().to_owned()),
Format::Dec => buf.append(&mut format!("{num}").as_bytes().to_owned()),
Format::Base64 => buf.append(
&mut fast32::base64::RFC4648
.encode(&split::unsigned_to_vec(num))
.as_bytes()
.to_owned(),
),
Format::Base32 => buf.append(
&mut fast32::base32::RFC4648
.encode(&split::unsigned_to_vec(num))
.as_bytes()
.to_owned(),
),
Format::Raw => buf.append(&mut split::unsigned_to_vec(num)),
}
buf
}
}
pub fn numf_parser_str<T>(s: &str) -> anyhow::Result<T>
where
T: std::str::FromStr + std::convert::TryFrom<u128>,
<T as std::str::FromStr>::Err: std::fmt::Display,
T: num::Num,
<T as num::Num>::FromStrRadixErr: std::fmt::Display,
<T as std::str::FromStr>::Err: std::fmt::Debug,
u128: std::convert::From<T>,
<T as std::str::FromStr>::Err: std::error::Error,
<T as std::convert::TryFrom<u128>>::Error: std::error::Error,
<T as std::convert::TryFrom<u128>>::Error: std::marker::Send,
<T as std::convert::TryFrom<u128>>::Error: std::marker::Sync,
<T as std::convert::TryFrom<u128>>::Error: 'static,
{
numf_parser(s.as_bytes())
}
pub fn numf_parser<T>(data: &[u8]) -> anyhow::Result<T>
where
T: std::str::FromStr + std::convert::TryFrom<u128>,
<T as std::str::FromStr>::Err: std::fmt::Display,
T: num::Num,
<T as num::Num>::FromStrRadixErr: std::fmt::Display,
<T as std::str::FromStr>::Err: std::fmt::Debug,
u128: std::convert::From<T>,
<T as std::str::FromStr>::Err: std::error::Error,
<T as std::convert::TryFrom<u128>>::Error: std::error::Error,
<T as std::convert::TryFrom<u128>>::Error: std::marker::Send,
<T as std::convert::TryFrom<u128>>::Error: std::marker::Sync,
<T as std::convert::TryFrom<u128>>::Error: 'static,
{
let data_as_text = String::from_utf8_lossy(data).to_string().replace("_", "");
if data_as_text.starts_with(&Format::Dec.prefix_str()) || data_as_text.parse::<T>().is_ok() {
let s = match data_as_text.strip_prefix(&Format::Dec.prefix_str()) {
Some(sr) => sr,
None => &data_as_text,
};
match s.parse() {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if data_as_text.starts_with(&Format::Hex.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Hex.prefix_str()) {
Some(sr) => sr,
None => &data_as_text,
};
match T::from_str_radix(s, 16) {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if data_as_text.starts_with(&Format::Octal.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Octal.prefix_str()) {
Some(sr) => sr,
None => &data_as_text,
};
match T::from_str_radix(s, 8) {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if data_as_text.starts_with(&Format::Bin.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Bin.prefix_str()) {
Some(sr) => sr,
None => &data_as_text,
};
match T::from_str_radix(s, 2) {
Ok(r) => Ok(r),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if data_as_text.starts_with(&Format::Base64.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Base64.prefix_str()) {
Some(sr) => sr,
None => &data_as_text,
};
match fast32::base64::RFC4648.decode_str(s) {
Ok(r) => Ok(join::array_to_unsigned::<T>(&r)?),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else if data_as_text.starts_with(&Format::Base32.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Base32.prefix_str()) {
Some(sr) => sr,
None => &data_as_text,
};
match fast32::base32::RFC4648.decode_str(s) {
Ok(r) => Ok(join::array_to_unsigned::<T>(&r)?),
Err(e) => {
let e = format!("{e}");
Err(anyhow!(e))
}
}
} else {
let s: Vec<u8> = if data.len() > 2 && data[0] == 0x00 {
data.iter().skip(1).map(ToOwned::to_owned).collect()
} else {
data.as_ref().to_vec()
};
Ok(join::array_to_unsigned(&s)?)
}
}