generated from PlexSheep/rs-base
Compare commits
8 commits
f3d7d807d5
...
335a5b2013
Author | SHA1 | Date | |
---|---|---|---|
335a5b2013 | |||
6b79c023bb | |||
fe71372de5 | |||
0261d74acd | |||
df32a863d6 | |||
45409c4ff3 | |||
24ad56c111 | |||
c18d1c09e9 |
6 changed files with 277 additions and 75 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "numf"
|
name = "numf"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = true
|
publish = true
|
||||||
authors = ["Christoph J. Scherr <software@cscherr.de>"]
|
authors = ["Christoph J. Scherr <software@cscherr.de>"]
|
||||||
|
|
|
@ -23,8 +23,9 @@ Current formats are:
|
||||||
- Decimal
|
- Decimal
|
||||||
- Base32
|
- Base32
|
||||||
- Base64
|
- Base64
|
||||||
|
- Raw
|
||||||
|
|
||||||
`numf` also has the option of prepending a prefix for each format, such as
|
`numf` also has the option of prepending a prefix for the formats, such as
|
||||||
`0x` for hexadecimal. Numbers may also be provided from the stdin. See `--help`
|
`0x` for hexadecimal. Numbers may also be provided from the stdin. See `--help`
|
||||||
flag for more information.
|
flag for more information.
|
||||||
|
|
||||||
|
|
265
src/format.rs
265
src/format.rs
|
@ -17,8 +17,18 @@
|
||||||
//! assert_eq!(Format::Base32.format_str(0x41414242, &options), "032sIFAUEQQ=");
|
//! assert_eq!(Format::Base32.format_str(0x41414242, &options), "032sIFAUEQQ=");
|
||||||
//! assert_eq!(Format::Base64.format_str(0x41414242, &options), "0sQUFCQg==");
|
//! assert_eq!(Format::Base64.format_str(0x41414242, &options), "0sQUFCQg==");
|
||||||
//! // sometimes you might need the raw bytes instead of a String
|
//! // sometimes you might need the raw bytes instead of a String
|
||||||
//! assert_eq!(Format::Raw.format(0x1337, &options), vec![0x13, 0x37]);
|
//! assert_eq!(Format::Raw.format(0x1337, &options), vec![0x00, 0x13, 0x37]);
|
||||||
//! assert_eq!(Format::Hex.format(0x1337, &options), vec![48, 120, 49, 51, 51, 55]);
|
//! assert_eq!(Format::Hex.format(0x1337, &options), vec![48, 120, 49, 51, 51, 55]);
|
||||||
|
//!
|
||||||
|
//! options.set_prefix(false);
|
||||||
|
//! options.set_padding(false);
|
||||||
|
//!
|
||||||
|
//! assert_eq!(Format::Hex.format_str(0x1337, &options), "1337");
|
||||||
|
//! assert_eq!(Format::Base32.format_str(0x41414242, &options), "IFAUEQQ=");
|
||||||
|
//! assert_eq!(Format::Base64.format_str(0x41414242, &options), "QUFCQg==");
|
||||||
|
//!
|
||||||
|
//! assert_eq!(Format::Raw.format(0x1337, &options), vec![0x13, 0x37]);
|
||||||
|
//! assert_eq!(Format::Hex.format(0x1337, &options), vec![49, 51, 51, 55]);
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
@ -34,7 +44,11 @@ use libpt::log::{debug, trace};
|
||||||
/// The number type [numf](crate) uses
|
/// The number type [numf](crate) uses
|
||||||
pub type NumberType = u128;
|
pub type NumberType = u128;
|
||||||
|
|
||||||
/// formats supported by numf
|
/// Describes a format for numbers
|
||||||
|
///
|
||||||
|
/// [Format] can be used to convert unsigned integers into a textual or other representation. See
|
||||||
|
/// [Format::format_str] for more. It is also possible to parse the various represenations to
|
||||||
|
/// a rust integer, see [numf_parser_str] for that.
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Default)]
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||||
pub enum Format {
|
pub enum Format {
|
||||||
Dec,
|
Dec,
|
||||||
|
@ -44,7 +58,7 @@ pub enum Format {
|
||||||
Octal,
|
Octal,
|
||||||
Base64,
|
Base64,
|
||||||
Base32,
|
Base32,
|
||||||
/// Write raw data to stdout, not text
|
/// Write raw data, not text
|
||||||
Raw,
|
Raw,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,11 +68,30 @@ impl Display for Format {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes what the formatter should do
|
/// Describes what the formatter should do exactly
|
||||||
///
|
///
|
||||||
/// Use [Self::default] to get a basic variant or create a object yourself.
|
/// Use [Self::default] to get a basic variant or create a object yourself.
|
||||||
///
|
///
|
||||||
/// This struct can be parsed with [clap] derive.
|
/// This struct can be parsed with [clap] derive.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use numf::format::{Format, FormatOptions};
|
||||||
|
/// let mut options = FormatOptions::default();
|
||||||
|
///
|
||||||
|
/// assert_eq!(Format::Bin.format_str(256, &options), "100000000");
|
||||||
|
/// assert_eq!(Format::Hex.format_str(256, &options), "100");
|
||||||
|
/// assert_eq!(Format::Base64.format_str(256, &options), "AQA=");
|
||||||
|
///
|
||||||
|
/// options.set_prefix(true);
|
||||||
|
/// options.set_padding(true);
|
||||||
|
///
|
||||||
|
/// assert_eq!(Format::Bin.format_str(256, &options), "0b0000000100000000");
|
||||||
|
/// assert_eq!(Format::Hex.format_str(256, &options), "0x0100");
|
||||||
|
/// assert_eq!(Format::Base64.format_str(256, &options), "0sAQA=");
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
#[derive(Parser, Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Parser, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about, long_about = None)]
|
||||||
#[command(
|
#[command(
|
||||||
|
@ -106,12 +139,12 @@ pub struct FormatOptions {
|
||||||
#[arg(short = 'a', long)]
|
#[arg(short = 'a', long)]
|
||||||
/// format raw, no text
|
/// format raw, no text
|
||||||
raw: bool,
|
raw: bool,
|
||||||
#[arg(short = 'r', long, default_value_t = 0, value_parser=numf_parser::<NumberType>)]
|
#[arg(short = 'r', long, default_value_t = 0, value_parser=numf_parser_str::<NumberType>)]
|
||||||
/// output random numbers
|
/// output random numbers
|
||||||
///
|
///
|
||||||
/// Add a user defined amount of cryptographically pseudorandom numbers to the number list.
|
/// Add a user defined amount of cryptographically pseudorandom numbers to the number list.
|
||||||
rand: NumberType,
|
rand: NumberType,
|
||||||
#[arg(long, default_value_t = NumberType::MAX, value_parser=numf_parser::<NumberType>)]
|
#[arg(long, default_value_t = NumberType::MAX, value_parser=numf_parser_str::<NumberType>)]
|
||||||
/// max for the random numbers
|
/// max for the random numbers
|
||||||
///
|
///
|
||||||
/// Generated numbers will not be lower than this. Only has an effect with --rand set.
|
/// Generated numbers will not be lower than this. Only has an effect with --rand set.
|
||||||
|
@ -119,7 +152,7 @@ pub struct FormatOptions {
|
||||||
#[arg(short = 'z', long)]
|
#[arg(short = 'z', long)]
|
||||||
/// format to base32
|
/// format to base32
|
||||||
base32: bool,
|
base32: bool,
|
||||||
#[clap(value_parser=numf_parser::<NumberType>, required=false)]
|
#[clap(value_parser=numf_parser_str::<NumberType>, required=false)]
|
||||||
/// numbers that should be formatted
|
/// numbers that should be formatted
|
||||||
///
|
///
|
||||||
/// Any of the [Formats](Format::format) are supported, but the prefixes are needed for formats
|
/// Any of the [Formats](Format::format) are supported, but the prefixes are needed for formats
|
||||||
|
@ -266,17 +299,44 @@ impl Default for FormatOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format {
|
impl Format {
|
||||||
|
/// Get the perfix for that [Format] as [Vec<u8>].
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use numf::format::Format;
|
||||||
|
/// assert_eq!(Format::Bin.prefix_str(), "0b");
|
||||||
|
/// assert_eq!(Format::Dec.prefix_str(), "0d");
|
||||||
|
/// assert_eq!(Format::Hex.prefix_str(), "0x");
|
||||||
|
/// assert_eq!(Format::Octal.prefix_str(), "0o");
|
||||||
|
/// assert_eq!(Format::Base64.prefix_str(), "0s");
|
||||||
|
/// assert_eq!(Format::Base32.prefix_str(), "032s");
|
||||||
|
/// assert_eq!(Format::Raw.prefix_str(), "\x00");
|
||||||
|
/// ```
|
||||||
pub fn prefix_str(&self) -> String {
|
pub fn prefix_str(&self) -> String {
|
||||||
String::from_utf8_lossy(&self.prefix()).to_string()
|
String::from_utf8_lossy(&self.prefix()).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the perfix for that [Format]
|
/// Get the perfix for that [Format] as [Vec<u8>].
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use numf::format::Format;
|
||||||
|
/// assert_eq!(Format::Bin.prefix(), b"0b");
|
||||||
|
/// assert_eq!(Format::Dec.prefix(), b"0d");
|
||||||
|
/// assert_eq!(Format::Hex.prefix(), b"0x");
|
||||||
|
/// assert_eq!(Format::Octal.prefix(), b"0o");
|
||||||
|
/// assert_eq!(Format::Base64.prefix(), b"0s");
|
||||||
|
/// assert_eq!(Format::Base32.prefix(), b"032s");
|
||||||
|
/// assert_eq!(Format::Raw.prefix(), vec![0x00]);
|
||||||
|
/// ```
|
||||||
pub fn prefix(&self) -> Vec<u8> {
|
pub fn prefix(&self) -> Vec<u8> {
|
||||||
match self {
|
match self {
|
||||||
// apperently used nowhere, sometimes 0 is used as a prefix but I
|
// apperently used nowhere, sometimes 0 is used as a prefix but I
|
||||||
// think this makes it more clear that this is decimal
|
// think this makes it more clear that this is decimal
|
||||||
Format::Dec => b"0d".to_vec(),
|
Format::Dec => b"0d".to_vec(),
|
||||||
Format::Raw => [].to_vec(), // TODO: find a better way to deal with this
|
Format::Raw => [0x00].to_vec(),
|
||||||
// very common
|
// very common
|
||||||
Format::Hex => b"0x".to_vec(),
|
Format::Hex => b"0x".to_vec(),
|
||||||
// very common
|
// very common
|
||||||
|
@ -289,17 +349,66 @@ impl Format {
|
||||||
Format::Base32 => b"032s".to_vec(),
|
Format::Base32 => b"032s".to_vec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// format a number with a [Format] and [FormatOptions] to [String]
|
/// format a number with a [Format] and [FormatOptions] to a [String]
|
||||||
|
///
|
||||||
|
/// If you need raw byte outputs, use [Format::format] instead.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use numf::format::{Format, FormatOptions};
|
||||||
|
/// let mut options = FormatOptions::default();
|
||||||
|
///
|
||||||
|
/// assert_eq!(Format::Bin.format_str(256, &options), "100000000");
|
||||||
|
/// assert_eq!(Format::Hex.format_str(256, &options), "100");
|
||||||
|
/// assert_eq!(Format::Base64.format_str(256, &options), "AQA=");
|
||||||
|
///
|
||||||
|
/// options.set_prefix(true);
|
||||||
|
/// options.set_padding(true);
|
||||||
|
///
|
||||||
|
/// assert_eq!(Format::Bin.format_str(256, &options), "0b0000000100000000");
|
||||||
|
/// assert_eq!(Format::Hex.format_str(256, &options), "0x0100");
|
||||||
|
/// assert_eq!(Format::Base64.format_str(256, &options), "0sAQA=");
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
pub fn format_str(&self, num: NumberType, options: &FormatOptions) -> String {
|
pub fn format_str(&self, num: NumberType, options: &FormatOptions) -> String {
|
||||||
String::from_utf8_lossy(&self.format(num, options)).to_string()
|
String::from_utf8_lossy(&self.format(num, options)).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// format a number with a [Format] and [FormatOptions]
|
/// format a number with a [Format] and [FormatOptions] to a byte vector [Vec<u8>]
|
||||||
|
///
|
||||||
|
/// If you need [String] outputs, use [Format::format_str] instead.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use numf::format::{Format, FormatOptions};
|
||||||
|
/// let mut options = FormatOptions::default();
|
||||||
|
///
|
||||||
|
/// assert_eq!(Format::Bin.format(256, &options), b"100000000");
|
||||||
|
/// assert_eq!(Format::Hex.format(256, &options), b"100");
|
||||||
|
/// assert_eq!(Format::Hex.format(256, &options), [49, 48, 48]);
|
||||||
|
/// assert_eq!(Format::Base64.format(256, &options), b"AQA=");
|
||||||
|
/// assert_eq!(Format::Raw.format(256, &options), [1, 0]);
|
||||||
|
///
|
||||||
|
/// options.set_prefix(true);
|
||||||
|
/// options.set_padding(true);
|
||||||
|
///
|
||||||
|
/// assert_eq!(Format::Bin.format(256, &options), b"0b0000000100000000");
|
||||||
|
/// assert_eq!(Format::Hex.format(256, &options), b"0x0100");
|
||||||
|
/// assert_eq!(Format::Hex.format(256, &options), [48, 120, 48, 49, 48, 48]);
|
||||||
|
/// assert_eq!(Format::Base64.format(256, &options), b"0sAQA=");
|
||||||
|
/// assert_eq!(Format::Raw.format(256, &options), [0, 1, 0]);
|
||||||
|
/// assert_eq!(Format::Raw.format(255, &options), [0, 255]);
|
||||||
|
/// assert_eq!(Format::Raw.format(32000, &options), [0, 125, 0]);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
pub fn format(&self, num: NumberType, options: &FormatOptions) -> Vec<u8> {
|
pub fn format(&self, num: NumberType, options: &FormatOptions) -> Vec<u8> {
|
||||||
debug!("formatting mode: {self}");
|
debug!("formatting mode: {self}");
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
if options.prefix() {
|
if options.prefix() {
|
||||||
buf.append(&mut self.prefix());
|
buf.append(&mut self.prefix());
|
||||||
|
debug!("prefix the buffer: {buf:X?}");
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
Format::Hex => {
|
Format::Hex => {
|
||||||
|
@ -335,21 +444,24 @@ impl Format {
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
// Format::Raw => buf.append(&mut split::unsigned_to_vec(num)),
|
// Format::Raw => buf.append(&mut split::unsigned_to_vec(num)),
|
||||||
Format::Raw => {
|
Format::Raw => buf.append(&mut split::unsigned_to_vec(num)),
|
||||||
debug!("do the raw thing");
|
|
||||||
buf.append(&mut split::unsigned_to_vec(num))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates an unsigned integer value that can be one of [Format](format::Format).
|
/// Converts a &[str] into an unsigned integer value (like [u128]), according to one of the [Formats](Format)
|
||||||
///
|
///
|
||||||
/// The number is assumed to be base-10 by default, it is parsed as a different
|
/// 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),
|
/// [Format] if the number is prefixed with the [prefix](FormatOptions::prefix),
|
||||||
/// case sensitive. So if the user inputs `0b1100` then this is parsed as
|
/// for that [Format]. So if the user inputs `0b1100` then this is parsed as
|
||||||
/// [Binary](format::Format::Bin) and so on.
|
/// [Binary](Format::Bin) and so on.
|
||||||
|
///
|
||||||
|
/// If you also want to parse raw inputs, use [numf_parser].
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// This parser will only output unsigned integers, it cannot be used with signed integers.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -359,17 +471,17 @@ impl Format {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use clap::Parser;
|
/// use clap::Parser;
|
||||||
/// use numf::format::numf_parser;
|
/// use numf::format::numf_parser_str;
|
||||||
///
|
///
|
||||||
/// #[derive(Parser)]
|
/// #[derive(Parser)]
|
||||||
/// struct Args {
|
/// struct Args {
|
||||||
/// #[clap(short, long, value_parser=numf_parser::<u128>)]
|
/// #[clap(short, long, value_parser=numf_parser_str::<u128>)]
|
||||||
/// address: u128,
|
/// address: u128,
|
||||||
/// }
|
/// }
|
||||||
/// let args = Args::parse_from(&["", "-a", "0x10"]);
|
/// let args = Args::parse_from(&["", "-a", "0x10"]);
|
||||||
/// assert_eq!(args.address, 16);
|
/// assert_eq!(args.address, 16);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn numf_parser<T>(s: &str) -> anyhow::Result<T>
|
pub fn numf_parser_str<T>(s: &str) -> anyhow::Result<T>
|
||||||
where
|
where
|
||||||
T: std::str::FromStr + std::convert::TryFrom<u128>,
|
T: std::str::FromStr + std::convert::TryFrom<u128>,
|
||||||
<T as std::str::FromStr>::Err: std::fmt::Display,
|
<T as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
|
@ -383,10 +495,68 @@ where
|
||||||
<T as std::convert::TryFrom<u128>>::Error: std::marker::Sync,
|
<T as std::convert::TryFrom<u128>>::Error: std::marker::Sync,
|
||||||
<T as std::convert::TryFrom<u128>>::Error: 'static,
|
<T as std::convert::TryFrom<u128>>::Error: 'static,
|
||||||
{
|
{
|
||||||
if s.starts_with(&Format::Dec.prefix_str()) || s.parse::<T>().is_ok() {
|
numf_parser(s.as_bytes())
|
||||||
let s = match s.strip_prefix(&Format::Dec.prefix_str()) {
|
}
|
||||||
|
|
||||||
|
/// Converts any data (as bytes) into an unsigned integer value `T` (like [u128]), according to one of the [Formats](Format)
|
||||||
|
///
|
||||||
|
/// If you only want to parse text data, use [numf_parser_str] instead.
|
||||||
|
///
|
||||||
|
/// The parser will first try to convert the data to a [String].
|
||||||
|
///
|
||||||
|
/// Then, the number is assumed to be base-10 by default, it is parsed as a different
|
||||||
|
/// [Format] if the number is prefixed with the [prefix](FormatOptions::prefix),
|
||||||
|
/// for that [Format]. So if the user inputs `0b1100` then this is parsed as
|
||||||
|
/// [Binary](Format::Bin) and so on.
|
||||||
|
///
|
||||||
|
/// If none of the text [Formats](Format) matches, the data will be assumed to be raw and converted
|
||||||
|
/// to the ingeger type directly.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If no text [Format] matches and the data is too long for the integer `T`.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// This parser will only output unsigned integers, it cannot be used with signed integers.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use numf::format::numf_parser;
|
||||||
|
///
|
||||||
|
/// let data = &[0x15, 0x92, 0xff];
|
||||||
|
/// let result: u64 = 0x1592ff;
|
||||||
|
/// assert_eq!(result, numf_parser(data).unwrap());
|
||||||
|
///
|
||||||
|
/// let data = b"0x1337";
|
||||||
|
/// let result: u64 = 0x1337;
|
||||||
|
/// assert_eq!(result, numf_parser(data).unwrap());
|
||||||
|
///
|
||||||
|
/// let data = b"0b110011";
|
||||||
|
/// let result: u64 = 0b110011;
|
||||||
|
/// assert_eq!(result, numf_parser(data).unwrap());
|
||||||
|
/// ```
|
||||||
|
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();
|
||||||
|
|
||||||
|
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,
|
Some(sr) => sr,
|
||||||
None => s,
|
None => &data_as_text,
|
||||||
};
|
};
|
||||||
match s.parse() {
|
match s.parse() {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
|
@ -395,10 +565,10 @@ where
|
||||||
Err(anyhow!(e))
|
Err(anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if s.starts_with(&Format::Hex.prefix_str()) {
|
} else if data_as_text.starts_with(&Format::Hex.prefix_str()) {
|
||||||
let s = match s.strip_prefix(&Format::Hex.prefix_str()) {
|
let s = match data_as_text.strip_prefix(&Format::Hex.prefix_str()) {
|
||||||
Some(sr) => sr,
|
Some(sr) => sr,
|
||||||
None => s,
|
None => &data_as_text,
|
||||||
};
|
};
|
||||||
match T::from_str_radix(s, 16) {
|
match T::from_str_radix(s, 16) {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
|
@ -407,10 +577,10 @@ where
|
||||||
Err(anyhow!(e))
|
Err(anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if s.starts_with(&Format::Octal.prefix_str()) {
|
} else if data_as_text.starts_with(&Format::Octal.prefix_str()) {
|
||||||
let s = match s.strip_prefix(&Format::Octal.prefix_str()) {
|
let s = match data_as_text.strip_prefix(&Format::Octal.prefix_str()) {
|
||||||
Some(sr) => sr,
|
Some(sr) => sr,
|
||||||
None => s,
|
None => &data_as_text,
|
||||||
};
|
};
|
||||||
match T::from_str_radix(s, 8) {
|
match T::from_str_radix(s, 8) {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
|
@ -419,10 +589,10 @@ where
|
||||||
Err(anyhow!(e))
|
Err(anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if s.starts_with(&Format::Bin.prefix_str()) {
|
} else if data_as_text.starts_with(&Format::Bin.prefix_str()) {
|
||||||
let s = match s.strip_prefix(&Format::Bin.prefix_str()) {
|
let s = match data_as_text.strip_prefix(&Format::Bin.prefix_str()) {
|
||||||
Some(sr) => sr,
|
Some(sr) => sr,
|
||||||
None => s,
|
None => &data_as_text,
|
||||||
};
|
};
|
||||||
match T::from_str_radix(s, 2) {
|
match T::from_str_radix(s, 2) {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
|
@ -431,10 +601,10 @@ where
|
||||||
Err(anyhow!(e))
|
Err(anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if s.starts_with(&Format::Base64.prefix_str()) {
|
} else if data_as_text.starts_with(&Format::Base64.prefix_str()) {
|
||||||
let s = match s.strip_prefix(&Format::Base64.prefix_str()) {
|
let s = match data_as_text.strip_prefix(&Format::Base64.prefix_str()) {
|
||||||
Some(sr) => sr,
|
Some(sr) => sr,
|
||||||
None => s,
|
None => &data_as_text,
|
||||||
};
|
};
|
||||||
match fast32::base64::RFC4648.decode_str(s) {
|
match fast32::base64::RFC4648.decode_str(s) {
|
||||||
Ok(r) => Ok(join::array_to_unsigned::<T>(&r)?),
|
Ok(r) => Ok(join::array_to_unsigned::<T>(&r)?),
|
||||||
|
@ -443,10 +613,10 @@ where
|
||||||
Err(anyhow!(e))
|
Err(anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if s.starts_with(&Format::Base32.prefix_str()) {
|
} else if data_as_text.starts_with(&Format::Base32.prefix_str()) {
|
||||||
let s = match s.strip_prefix(&Format::Base32.prefix_str()) {
|
let s = match data_as_text.strip_prefix(&Format::Base32.prefix_str()) {
|
||||||
Some(sr) => sr,
|
Some(sr) => sr,
|
||||||
None => s,
|
None => &data_as_text,
|
||||||
};
|
};
|
||||||
match fast32::base32::RFC4648.decode_str(s) {
|
match fast32::base32::RFC4648.decode_str(s) {
|
||||||
Ok(r) => Ok(join::array_to_unsigned::<T>(&r)?),
|
Ok(r) => Ok(join::array_to_unsigned::<T>(&r)?),
|
||||||
|
@ -455,14 +625,13 @@ where
|
||||||
Err(anyhow!(e))
|
Err(anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if s.starts_with(&Format::Raw.prefix_str()) {
|
|
||||||
let s = match s.strip_prefix(&Format::Raw.prefix_str()) {
|
|
||||||
Some(sr) => sr,
|
|
||||||
None => s,
|
|
||||||
};
|
|
||||||
todo!("reading raw not implemented")
|
|
||||||
} else {
|
} else {
|
||||||
let e = "could not determine the format of the value".to_string();
|
// what could go wrong with interpreting everything else as raw number input
|
||||||
Err(anyhow!(e))
|
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)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
//! Format numbers
|
//! Format numbers
|
||||||
//!
|
//!
|
||||||
//! This crate contains several utility functions for formatting numbers
|
//! This crate contains several utility functions for formatting numbers
|
||||||
//! into other systems, such as converting decimal numbers to hexadecimal.
|
//! into other systems, such as converting decimal numbers to hexadecimal
|
||||||
|
//! , and back.
|
||||||
//!
|
//!
|
||||||
//! See [format::Format] for supported formats.
|
//! See [format::Format] for supported formats.
|
||||||
//!
|
//!
|
||||||
//! Note that this crate is primarily intended to be used as a executable.
|
//! Note that this crate is primarily intended to be used as a executable.
|
||||||
|
//!
|
||||||
|
//! Highlights:
|
||||||
|
//! * [format::numf_parser]
|
||||||
|
//! * [format::numf_parser_str]
|
||||||
|
//! * [format::Format::format]
|
||||||
|
//! * [format::Format::format_str]
|
||||||
|
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -2,11 +2,12 @@ use std::io::{IsTerminal, Read, Write};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser};
|
||||||
|
use numf::format::numf_parser_str;
|
||||||
|
|
||||||
mod format;
|
mod format;
|
||||||
use crate::format::{numf_parser, Format};
|
use crate::format::{numf_parser, Format};
|
||||||
use format::*;
|
use format::*;
|
||||||
use libpt::log::debug;
|
use libpt::log::{debug, error};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
// try to read from stdin first, appending the numbers we read to the FormatOptions
|
// try to read from stdin first, appending the numbers we read to the FormatOptions
|
||||||
|
@ -16,7 +17,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
.display_time(false)
|
.display_time(false)
|
||||||
.build()
|
.build()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
eprintln!("could not initialize logger: {e}");
|
error!("could not initialize logger: {e}");
|
||||||
});
|
});
|
||||||
debug!("logger active");
|
debug!("logger active");
|
||||||
|
|
||||||
|
@ -26,21 +27,28 @@ fn main() -> anyhow::Result<()> {
|
||||||
if !stdin.is_terminal() {
|
if !stdin.is_terminal() {
|
||||||
match stdin.lock().read_to_end(&mut stdin_nums) {
|
match stdin.lock().read_to_end(&mut stdin_nums) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let whole: String = match String::from_utf8(stdin_nums) {
|
let whole: String = match String::from_utf8(stdin_nums.clone()) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
eprintln!("{}", FormatOptions::command().render_usage());
|
let number = match numf_parser(&stdin_nums) {
|
||||||
eprintln!("stdin for this program only accepts text: {e:#?}");
|
Ok(n) => n,
|
||||||
exit(1);
|
Err(e) => {
|
||||||
|
eprintln!("{}", FormatOptions::command().render_usage());
|
||||||
|
error!("could raw inputs from stdin as numbers: {e:#?}");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
options.push_number(number);
|
||||||
|
String::new()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let split = whole.split_whitespace();
|
let split = whole.split_whitespace();
|
||||||
for s in split {
|
for s in split {
|
||||||
let number = match numf_parser(s) {
|
let number = match numf_parser_str(s) {
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", FormatOptions::command().render_usage());
|
eprintln!("{}", FormatOptions::command().render_usage());
|
||||||
eprintln!("could not parse number from stdin: {e:#?}");
|
error!("could not parse number from stdin: {e:#?}");
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -49,7 +57,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", FormatOptions::command().render_usage());
|
eprintln!("{}", FormatOptions::command().render_usage());
|
||||||
eprintln!("could not read from stdin: {e:#?}");
|
error!("could not read from stdin: {e:#?}");
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -67,7 +75,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
// exit with error if no numbers are to be formatted
|
// exit with error if no numbers are to be formatted
|
||||||
if options.numbers().is_empty() {
|
if options.numbers().is_empty() {
|
||||||
eprintln!("{}", FormatOptions::command().render_usage());
|
eprintln!("{}", FormatOptions::command().render_usage());
|
||||||
eprintln!("no numbers have been provided");
|
error!("no numbers have been provided");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,9 @@ fn format() {
|
||||||
Format::Base64.format_str(0x4141414141414141, &options),
|
Format::Base64.format_str(0x4141414141414141, &options),
|
||||||
"QUFBQUFBQUE="
|
"QUFBQUFBQUE="
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(Format::Raw.format(0x1337, &options), vec![0x13, 0x37]);
|
||||||
|
assert_eq!(Format::Raw.format(0x0, &options), vec![0x0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -91,6 +94,9 @@ fn format_padding() {
|
||||||
Format::Base64.format_str(0x4141414141414141, &options),
|
Format::Base64.format_str(0x4141414141414141, &options),
|
||||||
"QUFBQUFBQUE="
|
"QUFBQUFBQUE="
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(Format::Raw.format(0x1337, &options), vec![0x13, 0x37]);
|
||||||
|
assert_eq!(Format::Raw.format(0x0, &options), vec![0x0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -142,6 +148,9 @@ fn format_prefix() {
|
||||||
Format::Base64.format_str(0x4141414141414141, &options),
|
Format::Base64.format_str(0x4141414141414141, &options),
|
||||||
"0sQUFBQUFBQUE="
|
"0sQUFBQUFBQUE="
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(Format::Raw.format(0x1337, &options), vec![0x0, 0x13, 0x37]);
|
||||||
|
assert_eq!(Format::Raw.format(0x0, &options), vec![0x0, 0x0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -199,6 +208,9 @@ fn format_padded_prefix() {
|
||||||
Format::Base64.format_str(0x4141414141414141, &options),
|
Format::Base64.format_str(0x4141414141414141, &options),
|
||||||
"0sQUFBQUFBQUE="
|
"0sQUFBQUFBQUE="
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(Format::Raw.format(0x1337, &options), vec![0x0, 0x13, 0x37]);
|
||||||
|
assert_eq!(Format::Raw.format(0x0, &options), vec![0x0, 0x0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -215,41 +227,46 @@ fn set_format_checker() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_dec() {
|
fn parser_dec() {
|
||||||
assert_eq!(numf_parser::<u32>("1337").unwrap(), 1337);
|
assert_eq!(numf_parser_str::<u32>("1337").unwrap(), 1337);
|
||||||
assert_eq!(numf_parser::<u32>("0d1337").unwrap(), 1337);
|
assert_eq!(numf_parser_str::<u32>("0d1337").unwrap(), 1337);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_bin() {
|
fn parser_bin() {
|
||||||
assert_eq!(numf_parser::<u32>("0b11001").unwrap(), 0b11001);
|
assert_eq!(numf_parser_str::<u32>("0b11001").unwrap(), 0b11001);
|
||||||
assert_eq!(numf_parser::<u32>("0b11001").unwrap(), 0b11001);
|
assert_eq!(numf_parser_str::<u32>("0b11001").unwrap(), 0b11001);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_hex() {
|
fn parser_hex() {
|
||||||
assert_eq!(numf_parser::<u32>("0xdeadbeef").unwrap(), 0xdeadbeef);
|
assert_eq!(numf_parser_str::<u32>("0xdeadbeef").unwrap(), 0xdeadbeef);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_oct() {
|
fn parser_oct() {
|
||||||
assert_eq!(numf_parser::<u32>("0o771171").unwrap(), 0o771171);
|
assert_eq!(numf_parser_str::<u32>("0o771171").unwrap(), 0o771171);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_b64() {
|
fn parser_b64() {
|
||||||
assert_eq!(numf_parser::<u32>("0sQUFCQg==").unwrap(), 0x41414242);
|
assert_eq!(numf_parser_str::<u32>("0sQUFCQg==").unwrap(), 0x41414242);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_b32() {
|
fn parser_b32() {
|
||||||
assert_eq!(numf_parser::<u32>("032sIFAUEQQ=").unwrap(), 0x41414242);
|
assert_eq!(numf_parser_str::<u32>("032sIFAUEQQ=").unwrap(), 0x41414242);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parser_raw() {
|
||||||
|
assert_eq!(numf_parser_str::<u32>("\x00\x50\x60").unwrap(), 0x5060);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_generics() {
|
fn parser_generics() {
|
||||||
assert_eq!(numf_parser::<u8>("55").unwrap(), 55);
|
assert_eq!(numf_parser_str::<u8>("55").unwrap(), 55);
|
||||||
assert_eq!(numf_parser::<u16>("55").unwrap(), 55);
|
assert_eq!(numf_parser_str::<u16>("55").unwrap(), 55);
|
||||||
assert_eq!(numf_parser::<u32>("55").unwrap(), 55);
|
assert_eq!(numf_parser_str::<u32>("55").unwrap(), 55);
|
||||||
assert_eq!(numf_parser::<u64>("55").unwrap(), 55);
|
assert_eq!(numf_parser_str::<u64>("55").unwrap(), 55);
|
||||||
assert_eq!(numf_parser::<u128>("55").unwrap(), 55);
|
assert_eq!(numf_parser_str::<u128>("55").unwrap(), 55);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue