bytes: Added support for decimal values (e.g. 1.2k)

Added use of `f128` internally (default feature gated.)

Fortune for units's current commit: Small blessing − 小吉
master
Avril 2 weeks ago
parent 91276ef928
commit 5cde9e6885
Signed by: flanchan
GPG Key ID: 284488987C31F630

25
Cargo.lock generated

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -8,40 +10,31 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "rustc_version"
version = "0.2.3"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "smallmap"
version = "1.3.0"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d61d73d5986b7f728a76234ca60f7826faa44dd9043d2954ca6583bfc7b875d"
checksum = "d6d0205d551de9bbfea07e51ace307c7f1ca224958a3df56fef6c2cfd244c199"
dependencies = [
"rustc_version",
]
[[package]]
name = "units"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"lazy_static",
"smallmap",

@ -1,11 +1,25 @@
[package]
name = "units"
version = "0.1.0"
version = "0.2.0"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
# TODO: Do we want to strip symbols if we plan to support loading plugins that can register units?
strip = true
[features]
default=["f128-math"]
# Use 128-bit floating-point math internally where possible.
f128-math = []
[dependencies]
lazy_static = "1.4.0"
smallmap = "1.3.0"

@ -4,10 +4,15 @@ use smallmap::Map;
#[derive(Debug)]
pub struct Bytes;
const KB: u64 = 1024;
type BytesUnit = u128;
type IntermediateUnit = f64;
const KB: BytesUnit = 1024;
lazy_static! {
static ref SUFFIX: Map<char, u64> = smallmap::smallmap! {
static ref SUFFIX: Map<char, BytesUnit> = smallmap::smallmap! {
{'b' => 1},
{'k' => KB},
{'m' => KB * KB},
{'g' => KB * KB * KB},
@ -15,27 +20,46 @@ lazy_static! {
};
}
#[cfg(feature="f128-math")]
fn multiply(o: IntermediateUnit, u: BytesUnit) -> BytesUnit
{
(o as f128 * (u as f128)).ceil() as BytesUnit
}
#[cfg(not(feature="f128-math"))]
fn multiply(o: IntermediateUnit, u: BytesUnit) -> BytesUnit
{
(o * (u as f64)).ceil() as BytesUnit
}
impl Conversion for Bytes
{
const UNIT: &'static str = "bytes";
type Output = u64;
type Output = BytesUnit;
type Unit = BytesUnit;
fn convert(&self, input: &str) -> Result<(Self::Output, Option<Self::Unit>), Self::Error>
{
#[macro_export] macro_rules! parse {
($non:expr) => (($non).parse::<u64>().map_err(|e| ConversionError(format!("Failed to parse u64: {}", e)))?)
//TODO: Apparently, we cannot parse `f128` from string? See if there's another better way of doing this, or if there are fractional types at all...
macro_rules! parse {
($non:ident: $type:ty) => (($non).parse::<$type>().map_err(|e| ConversionError::new_for(self, format!("Failed to parse {}: {}", std::any::type_name::<$type>(), e)))?);
($non:ident) => (parse!($non: Self::Output));
}
let (o, u) = match input.char_indices().last().map(|(idx, chr)| (chr.to_lowercase().next().unwrap(), &input[..idx])) {
Some((chr, _)) if chr.is_numeric() => (parse!(input), 1),
Some((chr, _)) if chr.is_numeric() => return Ok((parse!(input: Self::Output), Some(1))),
Some((ref chr, non)) if SUFFIX.contains_key(chr) => (parse!(non), *SUFFIX.get(chr).unwrap()),
Some((ref chr, non)) if SUFFIX.contains_key(chr) => (parse!(non: IntermediateUnit), *SUFFIX.get(chr).unwrap()),
Some((chr, _)) => return Err(ConversionError(format!("Unknown suffix {}", chr))),
None => (0, 1),
Some((chr, _)) => return Err(ConversionError::new_for(self, format!("Unknown suffix {}", chr))),
None => (0 as IntermediateUnit, 1),
};
Ok((o * u, Some(u)))
if o.is_sign_negative() {
return Err(ConversionError::new_for(self, "Negative byte offsets not supported"));
}
Ok((multiply(o, u), Some(u)))
}
}

@ -1,5 +1,6 @@
use std::error;
use std::fmt;
use std::borrow::Cow;
use std::convert::Infallible;
pub mod bytes;
@ -16,14 +17,38 @@ pub trait Conversion
}
#[derive(Debug)]
pub struct ConversionError(String);
pub struct ConversionError
{
message: Box<str>,
unit: Cow<'static, str>,
}
impl ConversionError
{
#[inline]
pub fn new_for<C: ?Sized + Conversion>(_conv: &C, message: impl Into<Box<str>>) -> Self
{
Self {
message: message.into(),
unit: Cow::Borrowed(C::UNIT),
}
}
#[inline]
pub fn new(unit: impl ToString, message: impl Into<Box<str>>) -> Self
{
Self {
message: message.into(),
unit: Cow::Owned(unit.to_string()),
}
}
}
impl error::Error for ConversionError{}
impl fmt::Display for ConversionError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "conversion failed: {}", self.0)
write!(f, "conversion of {} failed: {}", self.unit, self.message)
}
}
@ -51,7 +76,7 @@ pub fn dispatch_out(unit: impl AsRef<str>, value: impl AsRef<str>) -> Result<(),
{
match unit.as_ref() {
bytes::Bytes::UNIT => print_output(bytes::Bytes.convert(value.as_ref())?),
name => return Err(ConversionError(format!("Unknown unit {}", name))),
name => return Err(ConversionError::new(name, "Unknown unit")),
}
Ok(())

@ -1,8 +1,9 @@
#![feature(associated_type_defaults)]
#![cfg_attr(feature="f128-math", feature(f128))]
#[macro_use] extern crate lazy_static;
#[macro_export] macro_rules! unwrap {
macro_rules! unwrap {
($err:expr) => {
match $err {
Ok(v) => v,

Loading…
Cancel
Save