Compare commits

..

No commits in common. "master" and "v0.4.0" have entirely different histories.

10 changed files with 131 additions and 372 deletions

View File

@ -0,0 +1,46 @@
name: cargo devel CI
on:
push:
branches:
- '**'
# - '!master'
jobs:
format:
name: cargo CI
permissions:
# Give the default GITHUB_TOKEN write permission to commit and push the
# added or changed files to the repository.
contents: write
steps:
- name: get repo
uses: actions/checkout@v4
- name: install rust
uses: https://github.com/dtolnay/rust-toolchain@stable
- name: install additional rust things
run: |
rustup component add rustfmt
rustup component add clippy
- name: config custom registry
run: |
mkdir -p ~/.cargo/
echo "" > ~/.cargo/config.toml
echo "[registry]" >> ~/.cargo/config.toml
echo 'cscherr = "cscherr"' >> ~/.cargo/config.toml
echo '[registries.cscherr]' >> ~/.cargo/config.toml
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
cat ~/.cargo/config.toml
- name: cargo clippy check
run: cargo clippy --all-features --all-targets --workspace
- name: cargo clippy fix
run: cargo clippy --fix --all-features --all-targets --workspace
- name: cargo fmt
run: cargo fmt --all
- name: cargo test
run: cargo test --all-features --all-targets --workspace && cargo test --all-features --workspace --doc
- name: commit back to repository
uses: https://github.com/stefanzweifel/git-auto-commit-action@v5
with:
# Optional. Commit message for the created commit.
# Defaults to "Apply automatic changes"
commit_message: automatic cargo CI changes

View File

@ -1,7 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
# Check for updates every Monday
schedule:
interval: "weekly"

View File

@ -1,16 +1,10 @@
name: Rust CI name: cargo devel CI
on: on:
pull_request:
branches:
- '**'
push: push:
branches: branches:
- '**' - '**'
# - '!master' # - '!master'
env:
CARGO_TERM_COLOR: always
jobs: jobs:
CI: CI:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -44,10 +38,10 @@ jobs:
- name: cargo fmt - name: cargo fmt
run: cargo fmt --all run: cargo fmt --all
- name: cargo test - name: cargo test
run: cargo test --all-features --all-targets --workspace run: cargo test --all-features --all-targets --workspace && cargo test --all-features --workspace --doc
- name: commit back to repository - name: commit back to repository
uses: stefanzweifel/git-auto-commit-action@v5 uses: stefanzweifel/git-auto-commit-action@v5
with: with:
# Optional. Commit message for the created commit. # Optional. Commit message for the created commit.
# Defaults to "Apply automatic changes" # Defaults to "Apply automatic changes"
commit_message: "ci: automatic Rust CI changes" commit_message: automatic cargo CI changes

View File

@ -1,54 +0,0 @@
name: Release-plz
permissions:
pull-requests: write
contents: write
on:
push:
branches:
- master
- main
jobs:
# Release unpublished packages.
release-plz-release:
name: Release-plz release
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run release-plz
uses: MarcoIeni/release-plz-action@v0.5
with:
command: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
# Create a PR with the new versions and changelog, preparing the next release.
release-plz-pr:
name: Release-plz PR
runs-on: ubuntu-latest
concurrency:
group: release-plz-${{ github.ref }}
cancel-in-progress: false
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run release-plz
uses: MarcoIeni/release-plz-action@v0.5
with:
command: release-pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "numf" name = "numf"
version = "0.4.1" version = "0.4.0"
edition = "2021" edition = "2021"
publish = true publish = true
authors = ["Christoph J. Scherr <software@cscherr.de>"] authors = ["Christoph J. Scherr <software@cscherr.de>"]

View File

@ -1,12 +1,15 @@
# numf # numf
![Project badge](https://img.shields.io/badge/language-Rust-blue.svg) ![Project badge](https://img.shields.io/badge/language-Rust-blue.svg)
![Crates.io License](https://img.shields.io/crates/l/numf) ![Crates.io License](https://img.shields.io/crates/l/numf)
![GitHub Release](https://img.shields.io/github/v/release/PlexSheep/numf) ![Gitea Release](https://img.shields.io/gitea/v/release/PlexSheep/numf?gitea_url=https%3A%2F%2Fgit.cscherr.de)
![GitHub language count](https://img.shields.io/github/languages/count/PlexSheep/numf) ![Gitea language count](https://img.shields.io/gitea/languages/count/PlexSheep/numf?gitea_url=https%3A%2F%2Fgit.cscherr.de)
[![Rust CI](https://github.com/PlexSheep/numf/actions/workflows/cargo.yaml/badge.svg)](https://github.com/PlexSheep/numf/actions/workflows/cargo.yaml) [![cargo checks and tests](https://github.com/PlexSheep/numf/actions/workflows/cargo.yaml/badge.svg)](https://github.com/PlexSheep/numf/actions/workflows/cargo.yaml)
* [GitHub](https://github.com/PlexSheep/numf) * [Original Repository](https://git.cscherr.de/PlexSheep/numf)
* [GitHub Mirror](https://github.com/PlexSheep/numf)
* [Codeberg Mirror](https://codeberg.org/PlexSheep/numf)
* [crates.io](https://crates.io/crates/numf) * [crates.io](https://crates.io/crates/numf)
* [docs.rs](https://docs.rs/numf/latest/numf/) * [docs.rs](https://docs.rs/numf/latest/numf/)
@ -20,9 +23,8 @@ Current formats are:
- Decimal - Decimal
- Base32 - Base32
- Base64 - Base64
- Raw
`numf` also has the option of prepending a prefix for the formats, such as `numf` also has the option of prepending a prefix for each format, 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.
@ -33,27 +35,6 @@ $ numf -xp 1337 505 0xaabb
0x539 0x539
0x1F9 0x1F9
0xAABB 0xAABB
$ numf -a 505 | hedxump -C
00000000 01 f9 |..|
00000002
$ numf -a 505 | numf
1F9
$ numf -a 505 | numf -d
505
$ numf -a 505 | numf -b
111111001
$ echo -ne "\x20\xff\xb4" | numf -xpP
0x20FFB4
$ echo -ne "\x20\xff\xb4" | numf -d
2162612
$ base64='aGVsbG8gd29ybGQuCg==' ; echo "0s$base64" | numf -d
8271117963529473544792763018762
$ base64='aGVsbG8gd29ybGQuCg==' ; echo "0s$base64" | numf -s
aGVsbG8gd29ybGQuCg==
$ echo "0b100100101010" | numf -d
2346
$ echo "0b100100101010" | numf -bPp
0b0000100100101010
``` ```
## Installing ## Installing

View File

@ -17,18 +17,8 @@
//! 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![0x00, 0x13, 0x37]);
//! 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::Raw.format(0x1337, &options), vec![0x13, 0x37]);
//! assert_eq!(Format::Hex.format(0x1337, &options), vec![49, 51, 51, 55]); //! assert_eq!(Format::Hex.format(0x1337, &options), vec![48, 120, 49, 51, 51, 55]);
//! ``` //! ```
#![allow(dead_code)] #![allow(dead_code)]
@ -44,11 +34,7 @@ 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;
/// Describes a format for numbers /// formats supported by numf
///
/// [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,
@ -58,7 +44,7 @@ pub enum Format {
Octal, Octal,
Base64, Base64,
Base32, Base32,
/// Write raw data, not text /// Write raw data to stdout, not text
Raw, Raw,
} }
@ -68,30 +54,11 @@ impl Display for Format {
} }
} }
/// Describes what the formatter should do exactly /// Describes what the formatter should do
/// ///
/// 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(
@ -139,12 +106,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_str::<NumberType>)] #[arg(short = 'r', long, default_value_t = 0, value_parser=numf_parser::<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_str::<NumberType>)] #[arg(long, default_value_t = NumberType::MAX, value_parser=numf_parser::<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.
@ -152,7 +119,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_str::<NumberType>, required=false)] #[clap(value_parser=numf_parser::<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
@ -299,44 +266,17 @@ 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] as [Vec<u8>]. /// Get the perfix for that [Format]
///
/// # 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 => [0x00].to_vec(), Format::Raw => [].to_vec(), // TODO: find a better way to deal with this
// very common // very common
Format::Hex => b"0x".to_vec(), Format::Hex => b"0x".to_vec(),
// very common // very common
@ -349,66 +289,17 @@ impl Format {
Format::Base32 => b"032s".to_vec(), Format::Base32 => b"032s".to_vec(),
} }
} }
/// format a number with a [Format] and [FormatOptions] to a [String] /// format a number with a [Format] and [FormatOptions] to [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] to a byte vector [Vec<u8>] /// format a number with a [Format] and [FormatOptions]
///
/// 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 => {
@ -444,24 +335,21 @@ 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 => buf.append(&mut split::unsigned_to_vec(num)), Format::Raw => {
debug!("do the raw thing");
buf.append(&mut split::unsigned_to_vec(num))
}
} }
buf buf
} }
} }
/// Converts a &[str] into an unsigned integer value (like [u128]), according to one of the [Formats](Format) /// 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 /// 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), /// [Format](format::Format) if the number is prefixed with the [prefix](format::FormatOptions::prefix),
/// for that [Format]. So if the user inputs `0b1100` then this is parsed as /// case sensitive. So if the user inputs `0b1100` then this is parsed as
/// [Binary](Format::Bin) and so on. /// [Binary](format::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
/// ///
@ -471,17 +359,17 @@ impl Format {
/// ///
/// ``` /// ```
/// use clap::Parser; /// use clap::Parser;
/// use numf::format::numf_parser_str; /// use numf::format::numf_parser;
/// ///
/// #[derive(Parser)] /// #[derive(Parser)]
/// struct Args { /// struct Args {
/// #[clap(short, long, value_parser=numf_parser_str::<u128>)] /// #[clap(short, long, value_parser=numf_parser::<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_str<T>(s: &str) -> anyhow::Result<T> pub fn numf_parser<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,
@ -495,68 +383,10 @@ 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,
{ {
numf_parser(s.as_bytes()) if s.starts_with(&Format::Dec.prefix_str()) || s.parse::<T>().is_ok() {
} 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 => &data_as_text, None => s,
}; };
match s.parse() { match s.parse() {
Ok(r) => Ok(r), Ok(r) => Ok(r),
@ -565,10 +395,10 @@ where
Err(anyhow!(e)) Err(anyhow!(e))
} }
} }
} else if data_as_text.starts_with(&Format::Hex.prefix_str()) { } else if s.starts_with(&Format::Hex.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Hex.prefix_str()) { let s = match s.strip_prefix(&Format::Hex.prefix_str()) {
Some(sr) => sr, Some(sr) => sr,
None => &data_as_text, None => s,
}; };
match T::from_str_radix(s, 16) { match T::from_str_radix(s, 16) {
Ok(r) => Ok(r), Ok(r) => Ok(r),
@ -577,10 +407,10 @@ where
Err(anyhow!(e)) Err(anyhow!(e))
} }
} }
} else if data_as_text.starts_with(&Format::Octal.prefix_str()) { } else if s.starts_with(&Format::Octal.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Octal.prefix_str()) { let s = match s.strip_prefix(&Format::Octal.prefix_str()) {
Some(sr) => sr, Some(sr) => sr,
None => &data_as_text, None => s,
}; };
match T::from_str_radix(s, 8) { match T::from_str_radix(s, 8) {
Ok(r) => Ok(r), Ok(r) => Ok(r),
@ -589,10 +419,10 @@ where
Err(anyhow!(e)) Err(anyhow!(e))
} }
} }
} else if data_as_text.starts_with(&Format::Bin.prefix_str()) { } else if s.starts_with(&Format::Bin.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Bin.prefix_str()) { let s = match s.strip_prefix(&Format::Bin.prefix_str()) {
Some(sr) => sr, Some(sr) => sr,
None => &data_as_text, None => s,
}; };
match T::from_str_radix(s, 2) { match T::from_str_radix(s, 2) {
Ok(r) => Ok(r), Ok(r) => Ok(r),
@ -601,10 +431,10 @@ where
Err(anyhow!(e)) Err(anyhow!(e))
} }
} }
} else if data_as_text.starts_with(&Format::Base64.prefix_str()) { } else if s.starts_with(&Format::Base64.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Base64.prefix_str()) { let s = match s.strip_prefix(&Format::Base64.prefix_str()) {
Some(sr) => sr, Some(sr) => sr,
None => &data_as_text, None => s,
}; };
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)?),
@ -613,10 +443,10 @@ where
Err(anyhow!(e)) Err(anyhow!(e))
} }
} }
} else if data_as_text.starts_with(&Format::Base32.prefix_str()) { } else if s.starts_with(&Format::Base32.prefix_str()) {
let s = match data_as_text.strip_prefix(&Format::Base32.prefix_str()) { let s = match s.strip_prefix(&Format::Base32.prefix_str()) {
Some(sr) => sr, Some(sr) => sr,
None => &data_as_text, None => s,
}; };
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)?),
@ -625,13 +455,14 @@ where
Err(anyhow!(e)) Err(anyhow!(e))
} }
} }
} else { } else if s.starts_with(&Format::Raw.prefix_str()) {
// what could go wrong with interpreting everything else as raw number input let s = match s.strip_prefix(&Format::Raw.prefix_str()) {
let s: Vec<u8> = if data.len() > 2 && data[0] == 0x00 { Some(sr) => sr,
data.iter().skip(1).map(ToOwned::to_owned).collect() None => s,
} else {
data.as_ref().to_vec()
}; };
Ok(join::array_to_unsigned(&s)?) todo!("reading raw not implemented")
} else {
let e = "could not determine the format of the value".to_string();
Err(anyhow!(e))
} }
} }

View File

@ -1,17 +1,10 @@
//! 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;

View File

@ -2,12 +2,11 @@ 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, error}; use libpt::log::debug;
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
@ -17,7 +16,7 @@ fn main() -> anyhow::Result<()> {
.display_time(false) .display_time(false)
.build() .build()
.map_err(|e| { .map_err(|e| {
error!("could not initialize logger: {e}"); eprintln!("could not initialize logger: {e}");
}); });
debug!("logger active"); debug!("logger active");
@ -27,28 +26,21 @@ 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.clone()) { let whole: String = match String::from_utf8(stdin_nums) {
Ok(r) => r, Ok(r) => r,
Err(_) => { Err(e) => {
let number = match numf_parser(&stdin_nums) { eprintln!("{}", FormatOptions::command().render_usage());
Ok(n) => n, eprintln!("stdin for this program only accepts text: {e:#?}");
Err(e) => { exit(1);
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_str(s) { let number = match numf_parser(s) {
Ok(n) => n, Ok(n) => n,
Err(e) => { Err(e) => {
eprintln!("{}", FormatOptions::command().render_usage()); eprintln!("{}", FormatOptions::command().render_usage());
error!("could not parse number from stdin: {e:#?}"); eprintln!("could not parse number from stdin: {e:#?}");
exit(2); exit(2);
} }
}; };
@ -57,7 +49,7 @@ fn main() -> anyhow::Result<()> {
} }
Err(e) => { Err(e) => {
eprintln!("{}", FormatOptions::command().render_usage()); eprintln!("{}", FormatOptions::command().render_usage());
error!("could not read from stdin: {e:#?}"); eprintln!("could not read from stdin: {e:#?}");
exit(2); exit(2);
} }
}; };
@ -75,7 +67,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());
error!("no numbers have been provided"); eprintln!("no numbers have been provided");
exit(1); exit(1);
} }

View File

@ -41,9 +41,6 @@ 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]
@ -94,9 +91,6 @@ 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]
@ -148,9 +142,6 @@ 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]
@ -208,9 +199,6 @@ 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]
@ -227,46 +215,41 @@ fn set_format_checker() {
#[test] #[test]
fn parser_dec() { fn parser_dec() {
assert_eq!(numf_parser_str::<u32>("1337").unwrap(), 1337); assert_eq!(numf_parser::<u32>("1337").unwrap(), 1337);
assert_eq!(numf_parser_str::<u32>("0d1337").unwrap(), 1337); assert_eq!(numf_parser::<u32>("0d1337").unwrap(), 1337);
} }
#[test] #[test]
fn parser_bin() { fn parser_bin() {
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); assert_eq!(numf_parser::<u32>("0b11001").unwrap(), 0b11001);
} }
#[test] #[test]
fn parser_hex() { fn parser_hex() {
assert_eq!(numf_parser_str::<u32>("0xdeadbeef").unwrap(), 0xdeadbeef); assert_eq!(numf_parser::<u32>("0xdeadbeef").unwrap(), 0xdeadbeef);
} }
#[test] #[test]
fn parser_oct() { fn parser_oct() {
assert_eq!(numf_parser_str::<u32>("0o771171").unwrap(), 0o771171); assert_eq!(numf_parser::<u32>("0o771171").unwrap(), 0o771171);
} }
#[test] #[test]
fn parser_b64() { fn parser_b64() {
assert_eq!(numf_parser_str::<u32>("0sQUFCQg==").unwrap(), 0x41414242); assert_eq!(numf_parser::<u32>("0sQUFCQg==").unwrap(), 0x41414242);
} }
#[test] #[test]
fn parser_b32() { fn parser_b32() {
assert_eq!(numf_parser_str::<u32>("032sIFAUEQQ=").unwrap(), 0x41414242); assert_eq!(numf_parser::<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_str::<u8>("55").unwrap(), 55); assert_eq!(numf_parser::<u8>("55").unwrap(), 55);
assert_eq!(numf_parser_str::<u16>("55").unwrap(), 55); assert_eq!(numf_parser::<u16>("55").unwrap(), 55);
assert_eq!(numf_parser_str::<u32>("55").unwrap(), 55); assert_eq!(numf_parser::<u32>("55").unwrap(), 55);
assert_eq!(numf_parser_str::<u64>("55").unwrap(), 55); assert_eq!(numf_parser::<u64>("55").unwrap(), 55);
assert_eq!(numf_parser_str::<u128>("55").unwrap(), 55); assert_eq!(numf_parser::<u128>("55").unwrap(), 55);
} }