Comments (2)
Ref:
Perhaps adding various methods similar to its Go counterpart.
Methods are not the hard part. The serialization and deserialization is, because it has to enforce all the rules that the type has. The deserializer has to replicate the parsing of the suffix, and the serializer has to perform canonicalization.
Eg, let's say we represent it as:
pub struct Quantity {
pub value: i64, // Incorporates the exponent
pub suffix: Suffix,
}
pub enum Suffix {
None,
Kibi,
Mibi,
Kilo,
Mega,
...
}
This cannot be naively serialized by just serializing the value
and the suffix
one after the other, because Quantity { value: 1000, suffix: Suffix::None }
needs to be serialized as "1e3"
and not "1000"
, Quantity { value: 1000, suffix: Suffix::Kilo }
needs to be serialized as "1M"
and not "1000k"
, etc.
All that logic is in the files I linked above plus the go-inf library, so all of that would need to be translated to Rust and would need to be kept in sync with changes.
That will be backwards compatible I think. Or perhaps even replacing the type itself to imitate Go more closely, however that will definitely be incompatible change.
Compatibility is not a concern. Almost every release of this crate is already semver-major.
from k8s-openapi.
$dayjob needed code for the specific purpose of deserializing a Quantity
into a u64
in a lossy, saturating way, so I ended up writing two versions related to that. Neither is complete enough for me to publish to crates.io, but I'm putting them here so that I don't lose them.
Version 1: Deserializes into a lossless typed representation. Conversion to `u64` doesn't support binary suffixes.
use std::{collections::VecDeque, str::FromStr};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Quantity {
pub sign: Sign,
pub integral: Vec<u8>,
pub fractional: Vec<u8>,
pub suffix: Suffix,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Sign {
Positive,
Negative,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Suffix {
BinarySI(BinarySISuffix),
DecimalExponent(DecimalExponentSuffix),
DecimalSI(DecimalSISuffix),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BinarySISuffix {
Kibi,
Mebi,
Gibi,
Tebi,
Pebi,
Exbi,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DecimalExponentSuffix {
pub sign: Sign,
pub digits: Vec<u8>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecimalSISuffix {
Nano,
Micro,
Milli,
None,
Kilo,
Mega,
Giga,
Tera,
Peta,
Exa,
}
fn split_first(s: &[u8]) -> Option<(u8, &[u8])> {
s.split_first().map(|(first, rest)| (*first, rest))
}
impl FromStr for Quantity {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let rest = &mut s.as_bytes();
let sign = parse_sign(rest);
let integral = parse_digits(rest).unwrap_or_default();
let decimal_point;
(decimal_point, *rest) = match split_first(*rest) {
Some((b'.', rest)) => (true, rest),
_ => (false, *rest),
};
let fractional = if decimal_point {
parse_digits(rest).unwrap_or_default()
} else if integral.is_empty() {
return Err(format!(
"expected signed number, found {:?}",
rest.escape_ascii().to_string(),
));
} else {
vec![]
};
let suffix = parse_suffix(rest)?;
Ok(Self {
sign,
integral,
fractional,
suffix,
})
}
}
fn parse_sign(rest: &mut &[u8]) -> Sign {
let sign;
(sign, *rest) = match split_first(*rest) {
Some((b'+', rest)) => (Sign::Positive, rest),
Some((b'-', rest)) => (Sign::Negative, rest),
_ => (Sign::Positive, *rest),
};
sign
}
fn parse_suffix(rest: &mut &[u8]) -> Result<Suffix, String> {
#[allow(clippy::match_same_arms)]
{
*rest = match split_first(rest) {
Some((b'K', b"i")) => return Ok(Suffix::BinarySI(BinarySISuffix::Kibi)),
Some((b'M', b"i")) => return Ok(Suffix::BinarySI(BinarySISuffix::Mebi)),
Some((b'G', b"i")) => return Ok(Suffix::BinarySI(BinarySISuffix::Gibi)),
Some((b'T', b"i")) => return Ok(Suffix::BinarySI(BinarySISuffix::Tebi)),
Some((b'P', b"i")) => return Ok(Suffix::BinarySI(BinarySISuffix::Pebi)),
Some((b'E', b"i")) => return Ok(Suffix::BinarySI(BinarySISuffix::Exbi)),
Some((b'n', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Nano)),
Some((b'u', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Micro)),
Some((b'm', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Milli)),
None => return Ok(Suffix::DecimalSI(DecimalSISuffix::None)),
Some((b'k', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Kilo)),
Some((b'M', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Mega)),
Some((b'G', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Giga)),
Some((b'T', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Tera)),
Some((b'P', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Peta)),
Some((b'E', b"")) => return Ok(Suffix::DecimalSI(DecimalSISuffix::Exa)),
Some((b'e' | b'E', rest)) => rest,
Some(_) => {
return Err(format!(
"expected suffix, found {:?}",
rest.escape_ascii().to_string(),
))
}
};
}
let sign = parse_sign(rest);
let digits = parse_digits(rest)?;
if !rest.is_empty() {
return Err(format!(
"trailing garbage: {:?}",
rest.escape_ascii().to_string()
));
}
Ok(Suffix::DecimalExponent(DecimalExponentSuffix {
sign,
digits,
}))
}
fn parse_digits(rest: &mut &[u8]) -> Result<Vec<u8>, String> {
let mut result = vec![];
loop {
let digit;
(digit, *rest) = match split_first(*rest) {
Some((digit @ b'0'..=b'9', rest)) => (Some(digit), rest),
_ if !result.is_empty() => (None, *rest),
_ => return Err("digits is empty".to_owned()),
};
if let Some(digit) = digit {
result.push(digit - b'0');
} else {
break;
}
}
Ok(result)
}
impl From<Quantity> for u64 {
fn from(quantity: Quantity) -> Self {
let Quantity {
sign,
mut integral,
fractional,
mut suffix,
} = quantity;
let mut fractional: VecDeque<_> = fractional.into();
if integral.iter().all(|&digit| digit == 0) && fractional.iter().all(|&digit| digit == 0) {
return 0;
}
if matches!(sign, Sign::Negative) {
return 0;
}
loop {
match suffix {
Suffix::BinarySI(_) => unimplemented!(),
Suffix::DecimalExponent(DecimalExponentSuffix { sign, digits }) => {
let value = digits.into_iter().fold(0_u8, |value, digit| {
value.saturating_mul(10).saturating_add(digit)
});
for _ in 0..value {
match sign {
Sign::Positive => integral.push(fractional.pop_front().unwrap_or(0)),
Sign::Negative => fractional.push_front(integral.pop().unwrap_or(0)),
}
}
break;
}
Suffix::DecimalSI(DecimalSISuffix::Nano) => {
fractional.push_front(integral.pop().unwrap_or(0));
fractional.push_front(integral.pop().unwrap_or(0));
fractional.push_front(integral.pop().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::Micro);
}
Suffix::DecimalSI(DecimalSISuffix::Micro) => {
fractional.push_front(integral.pop().unwrap_or(0));
fractional.push_front(integral.pop().unwrap_or(0));
fractional.push_front(integral.pop().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::Milli);
}
Suffix::DecimalSI(DecimalSISuffix::Milli) => {
fractional.push_front(integral.pop().unwrap_or(0));
fractional.push_front(integral.pop().unwrap_or(0));
fractional.push_front(integral.pop().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::None);
}
Suffix::DecimalSI(DecimalSISuffix::None) => break,
Suffix::DecimalSI(DecimalSISuffix::Kilo) => {
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::None);
}
Suffix::DecimalSI(DecimalSISuffix::Mega) => {
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::Kilo);
}
Suffix::DecimalSI(DecimalSISuffix::Giga) => {
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::Mega);
}
Suffix::DecimalSI(DecimalSISuffix::Tera) => {
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::Giga);
}
Suffix::DecimalSI(DecimalSISuffix::Peta) => {
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::Tera);
}
Suffix::DecimalSI(DecimalSISuffix::Exa) => {
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
integral.push(fractional.pop_front().unwrap_or(0));
suffix = Suffix::DecimalSI(DecimalSISuffix::Peta);
}
}
}
let mut integral = integral.into_iter().fold(0_u64, |value, digit| {
value.saturating_mul(10).saturating_add(digit.into())
});
if fractional.iter().any(|&digit| digit != 0) {
integral = integral.saturating_add(1);
}
integral
}
}
#[cfg(test)]
mod tests {
use super::*;
// Ref: https://github.com/kubernetes/kubernetes/blob/v1.29.1/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go
#[test]
fn ok() {
#[rustfmt::skip]
let test_cases = [
("0", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("1", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("10", 10, Quantity { sign: Sign::Positive, integral: vec![1, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("500", 500, Quantity { sign: Sign::Positive, integral: vec![5, 0, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("8000", 8000, Quantity { sign: Sign::Positive, integral: vec![8, 0, 0 ,0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("2", 2, Quantity { sign: Sign::Positive, integral: vec![2], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.1", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![1], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.03", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 3], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.004", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 4], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("9223372036854775808", 9223372036854775808, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("92233720368547758080", u64::MAX, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("922337203685477580800", u64::MAX, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8, 0, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("922337203685477580.8", 922337203685477581, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0], fractional: vec![8], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("92233720368547758.08", 92233720368547759, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8], fractional: vec![0, 8], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("-9223372036854775809", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 9], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("-92233720368547758090", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 9, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("-922337203685477580900", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 9, 0, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("-922337203685477580.9", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0], fractional: vec![9], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("-92233720368547758.09", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8], fractional: vec![0, 9], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.025Ti", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 2, 5], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
("1.025Ti", 0, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![0, 2, 5], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
("-1.025Ti", 0, Quantity { sign: Sign::Negative, integral: vec![1], fractional: vec![0, 2, 5], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
(".", 0, Quantity { sign: Sign::Positive, integral: vec![], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("-.", 0, Quantity { sign: Sign::Negative, integral: vec![], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("1E-3", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Negative, digits: vec![3] }) }),
("0", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0n", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Nano) }),
("0u", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Micro) }),
("0m", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Milli) }),
("0Ki", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Kibi) }),
("0k", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("0Mi", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Mebi) }),
("0M", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Mega) }),
("0Gi", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Gibi) }),
("0G", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Giga) }),
("0Ti", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
("0T", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Tera) }),
("1", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("1Ki", 0, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Kibi) }),
("8Ki", 0, Quantity { sign: Sign::Positive, integral: vec![8], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Kibi) }),
("7Mi", 0, Quantity { sign: Sign::Positive, integral: vec![7], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Mebi) }),
("6Gi", 0, Quantity { sign: Sign::Positive, integral: vec![6], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Gibi) }),
("5Ti", 0, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
("4Pi", 0, Quantity { sign: Sign::Positive, integral: vec![4], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Pebi) }),
("3Ei", 0, Quantity { sign: Sign::Positive, integral: vec![3], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Exbi) }),
("10Ti", 0, Quantity { sign: Sign::Positive, integral: vec![1, 0], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
("100Ti", 0, Quantity { sign: Sign::Positive, integral: vec![1, 0, 0], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
("5n", 1, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Nano) }),
("4u", 1, Quantity { sign: Sign::Positive, integral: vec![4], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Micro) }),
("3m", 1, Quantity { sign: Sign::Positive, integral: vec![3], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Milli) }),
("9", 9, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("8k", 8000, Quantity { sign: Sign::Positive, integral: vec![8], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("50k", 50000, Quantity { sign: Sign::Positive, integral: vec![5, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("7M", 7000000, Quantity { sign: Sign::Positive, integral: vec![7], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Mega) }),
("6G", 6000000000, Quantity { sign: Sign::Positive, integral: vec![6], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Giga) }),
("5T", 5000000000000, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Tera) }),
("40T", 40000000000000, Quantity { sign: Sign::Positive, integral: vec![4, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Tera) }),
("300T", 300000000000000, Quantity { sign: Sign::Positive, integral: vec![3, 0, 0], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Tera) }),
("2P", 2000000000000000, Quantity { sign: Sign::Positive, integral: vec![2], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Peta) }),
("1E", 1000000000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Exa) }),
("1E-3", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Negative, digits: vec![3] }) }),
("1e3", 1000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![3] }) }),
("1E6", 1000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![6] }) }),
("1e9", 1000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![9] }) }),
("1E12", 1000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 2] }) }),
("1e15", 1000000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 5] }) }),
("1E18", 1000000000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 8] }) }),
("1e14", 100000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 4] }) }),
("1e13", 10000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 3] }) }),
("1e15", 1000000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 5] }) }),
("1e3", 1000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![3] }) }),
("100.035k", 100035, Quantity { sign: Sign::Positive, integral: vec![1, 0, 0], fractional: vec![0, 3, 5], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("0.001", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.0005k", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 5], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("0.005", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 5], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.05", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 5], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.5", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.00050k", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 5, 0], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("0.00500", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 5, 0, 0], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.05000", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 5, 0, 0, 0], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.50000", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5, 0, 0, 0, 0], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.5e0", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![0] }) }),
("0.5e-1", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Negative, digits: vec![1] }) }),
("0.5e-2", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Negative, digits: vec![2] }) }),
("0.5e0", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![0] }) }),
("10.035M", 10035000, Quantity { sign: Sign::Positive, integral: vec![1, 0], fractional: vec![0, 3, 5], suffix: Suffix::DecimalSI(DecimalSISuffix::Mega) }),
("1.2e3", 1200, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![2], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![3] }) }),
("1.3E+6", 1300000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![3], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![6] }) }),
("1.40e9", 1400000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![4, 0], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![9] }) }),
("1.53E12", 1530000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![5, 3], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 2] }) }),
("1.6e15", 1600000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![6], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 5] }) }),
("1.7E18", 1700000000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![7], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Positive, digits: vec![1, 8] }) }),
("9.01", 10, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("8.1k", 8100, Quantity { sign: Sign::Positive, integral: vec![8], fractional: vec![1], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("7.123456M", 7123456, Quantity { sign: Sign::Positive, integral: vec![7], fractional: vec![1, 2, 3, 4, 5, 6], suffix: Suffix::DecimalSI(DecimalSISuffix::Mega) }),
("6.987654321G", 6987654321, Quantity { sign: Sign::Positive, integral: vec![6], fractional: vec![9, 8, 7, 6, 5, 4, 3, 2, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::Giga) }),
("5.444T", 5444000000000, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![4, 4, 4], suffix: Suffix::DecimalSI(DecimalSISuffix::Tera) }),
("40.1T", 40100000000000, Quantity { sign: Sign::Positive, integral: vec![4, 0], fractional: vec![1], suffix: Suffix::DecimalSI(DecimalSISuffix::Tera) }),
("300.2T", 300200000000000, Quantity { sign: Sign::Positive, integral: vec![3, 0, 0], fractional: vec![2], suffix: Suffix::DecimalSI(DecimalSISuffix::Tera) }),
("2.5P", 2500000000000000, Quantity { sign: Sign::Positive, integral: vec![2], fractional: vec![5], suffix: Suffix::DecimalSI(DecimalSISuffix::Peta) }),
("1.01E", 1010000000000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::Exa) }),
("3.001n", 1, Quantity { sign: Sign::Positive, integral: vec![3], fractional: vec![0, 0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::Nano) }),
("1.1E-9", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![1], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Negative, digits: vec![9] }) }),
("0.0000000001", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.0000000005", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 5], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.00000000050", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.5e-9", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], suffix: Suffix::DecimalExponent(DecimalExponentSuffix { sign: Sign::Negative, digits: vec![9] }) }),
("0.9n", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![9], suffix: Suffix::DecimalSI(DecimalSISuffix::Nano) }),
("0.00000012345", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("0.00000012354", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 1, 2, 3, 5, 4], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("9Ei", 0, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Exbi) }),
("9223372036854775807Ki", 0, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 7], fractional: vec![], suffix: Suffix::BinarySI(BinarySISuffix::Kibi) }),
("12E", 12000000000000000000, Quantity { sign: Sign::Positive, integral: vec![1, 2], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Exa) }),
("100.035Ki", 0, Quantity { sign: Sign::Positive, integral: vec![1, 0, 0], fractional: vec![0, 3, 5], suffix: Suffix::BinarySI(BinarySISuffix::Kibi) }),
("0.5Mi", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], suffix: Suffix::BinarySI(BinarySISuffix::Mebi) }),
("0.05Gi", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 5], suffix: Suffix::BinarySI(BinarySISuffix::Gibi) }),
("0.025Ti", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 2, 5], suffix: Suffix::BinarySI(BinarySISuffix::Tebi) }),
("0.000000000001Ki", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], suffix: Suffix::BinarySI(BinarySISuffix::Kibi) }),
(".001", 1, Quantity { sign: Sign::Positive, integral: vec![], fractional: vec![0, 0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
(".0001k", 1, Quantity { sign: Sign::Positive, integral: vec![], fractional: vec![0, 0, 0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::Kilo) }),
("1.", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("1.G", 1000000000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], suffix: Suffix::DecimalSI(DecimalSISuffix::Giga) }),
("9.01", 10, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
("-9.01", 0, Quantity { sign: Sign::Negative, integral: vec![9], fractional: vec![0, 1], suffix: Suffix::DecimalSI(DecimalSISuffix::None) }),
];
for (input, expected_u64, expected_quantity) in test_cases {
eprintln!("input: {input}");
let actual_quantity: Quantity = input.parse().unwrap();
assert_eq!(expected_quantity, actual_quantity);
if !matches!(actual_quantity.suffix, Suffix::BinarySI(_)) {
let actual_u64: u64 = actual_quantity.into();
assert_eq!(expected_u64, actual_u64);
}
}
}
// Ref: https://github.com/kubernetes/kubernetes/blob/v1.29.1/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go
#[test]
fn err() {
for (input, expected) in [
("1.1.M", r#"expected suffix, found ".M""#),
("1+1.0M", r#"expected suffix, found "+1.0M""#),
("0.1mi", r#"expected suffix, found "mi""#),
("0.1am", r#"expected suffix, found "am""#),
("aoeu", r#"expected signed number, found "aoeu""#),
(".5i", r#"expected suffix, found "i""#),
("1i", r#"expected suffix, found "i""#),
("-3.01i", r#"expected suffix, found "i""#),
("-3.01e-", r#"digits is empty"#),
(" 1", r#"expected signed number, found " 1""#),
("1 ", r#"expected suffix, found " ""#),
] {
eprintln!("input: {input}");
let actual = input.parse::<Quantity>().unwrap_err();
assert_eq!(expected, actual);
}
}
}
Version 2: Deserializes into a lossy typed representation that treats binary suffixes as the closest decimal prefix.
use std::{cmp::Ordering, collections::VecDeque, str::FromStr};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Quantity {
pub sign: Sign,
pub integral: Vec<u8>,
pub fractional: Vec<u8>,
pub exponent: i8,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Sign {
Positive,
Negative,
}
fn split_first(s: &[u8]) -> Option<(u8, &[u8])> {
s.split_first().map(|(first, rest)| (*first, rest))
}
impl FromStr for Quantity {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let rest = &mut s.as_bytes();
let sign = parse_sign(rest);
let integral = parse_digits(rest).unwrap_or_default();
let decimal_point;
(decimal_point, *rest) = match split_first(rest) {
Some((b'.', rest)) => (true, rest),
_ => (false, *rest),
};
let fractional = if decimal_point {
parse_digits(rest).unwrap_or_default()
} else if integral.is_empty() {
return Err(format!(
"expected signed number, found {:?}",
rest.escape_ascii().to_string(),
));
} else {
vec![]
};
let exponent = parse_exponent(rest)?;
Ok(Self {
sign,
integral,
fractional,
exponent,
})
}
}
fn parse_sign(rest: &mut &[u8]) -> Sign {
let sign;
(sign, *rest) = match split_first(rest) {
Some((b'+', rest)) => (Sign::Positive, rest),
Some((b'-', rest)) => (Sign::Negative, rest),
_ => (Sign::Positive, *rest),
};
sign
}
fn parse_exponent(rest: &mut &[u8]) -> Result<i8, String> {
#[allow(clippy::match_same_arms)]
{
*rest = match split_first(rest) {
// Binary suffixes are treated like decimal suffixes.
// This means the computed value will be a little smaller than the intended value,
// but the difference is small enough that it won't matter.
Some((b'K', b"i")) => return Ok(3),
Some((b'M', b"i")) => return Ok(6),
Some((b'G', b"i")) => return Ok(9),
Some((b'T', b"i")) => return Ok(12),
Some((b'P', b"i")) => return Ok(15),
Some((b'E', b"i")) => return Ok(18),
Some((b'n', b"")) => return Ok(-9),
Some((b'u', b"")) => return Ok(-6),
Some((b'm', b"")) => return Ok(-3),
None => return Ok(0),
Some((b'k', b"")) => return Ok(3),
Some((b'M', b"")) => return Ok(6),
Some((b'G', b"")) => return Ok(9),
Some((b'T', b"")) => return Ok(12),
Some((b'P', b"")) => return Ok(15),
Some((b'E', b"")) => return Ok(18),
Some((b'e' | b'E', rest)) => rest,
Some(_) => {
return Err(format!(
"expected suffix, found {:?}",
rest.escape_ascii().to_string(),
))
}
};
}
let sign = parse_sign(rest);
let digits = parse_digits(rest)?;
let value = digits.into_iter().fold(0_i8, |value, digit| {
let value = value.saturating_mul(10);
match sign {
Sign::Positive => value.saturating_add(digit.try_into().expect("digit is 0..=9")),
Sign::Negative => value.saturating_sub(digit.try_into().expect("digit is 0..=9")),
}
});
if !rest.is_empty() {
return Err(format!(
"trailing garbage: {:?}",
rest.escape_ascii().to_string(),
));
}
Ok(value)
}
fn parse_digits(rest: &mut &[u8]) -> Result<Vec<u8>, String> {
let mut result = vec![];
loop {
let digit;
(digit, *rest) = match split_first(rest) {
Some((digit @ b'0'..=b'9', rest)) => (Some(digit), rest),
_ if !result.is_empty() => (None, *rest),
_ => return Err("digits is empty".to_owned()),
};
if let Some(digit) = digit {
result.push(digit - b'0');
} else {
break;
}
}
Ok(result)
}
impl From<Quantity> for u64 {
fn from(quantity: Quantity) -> Self {
let Quantity {
sign,
integral,
fractional,
exponent,
} = quantity;
let mut fractional: VecDeque<_> = fractional.into();
if integral.iter().all(|&digit| digit == 0) && fractional.iter().all(|&digit| digit == 0) {
return 0;
}
if matches!(sign, Sign::Negative) {
return 0;
}
let mut result = integral.into_iter().fold(0_u64, |value, digit| {
value.saturating_mul(10).saturating_add(digit.into())
});
let mut has_fractional_digits = fractional.iter().any(|&digit| digit != 0);
match exponent.cmp(&0) {
Ordering::Less => {
for _ in exponent..0 {
let last_integer_digit = result % 10;
result /= 10;
has_fractional_digits |= last_integer_digit != 0;
}
}
Ordering::Equal => (),
Ordering::Greater => {
for _ in 0..exponent {
result = result
.saturating_mul(10)
.saturating_add(fractional.pop_front().unwrap_or(0).into());
}
has_fractional_digits = fractional.iter().any(|&digit| digit != 0);
}
}
if has_fractional_digits {
result = result.saturating_add(1);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
// Ref: https://github.com/kubernetes/kubernetes/blob/v1.29.1/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go
#[test]
fn ok() {
#[rustfmt::skip]
let test_cases = [
("0", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 0 }),
("1", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 0 }),
("10", 10, Quantity { sign: Sign::Positive, integral: vec![1, 0], fractional: vec![], exponent: 0 }),
("500", 500, Quantity { sign: Sign::Positive, integral: vec![5, 0, 0], fractional: vec![], exponent: 0 }),
("8000", 8_000, Quantity { sign: Sign::Positive, integral: vec![8, 0, 0 ,0], fractional: vec![], exponent: 0 }),
("2", 2, Quantity { sign: Sign::Positive, integral: vec![2], fractional: vec![], exponent: 0 }),
("0.1", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![1], exponent: 0 }),
("0.03", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 3], exponent: 0 }),
("0.004", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 4], exponent: 0 }),
("9223372036854775808", 9_223_372_036_854_775_808, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8], fractional: vec![], exponent: 0 }),
("92233720368547758080", u64::MAX, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8, 0], fractional: vec![], exponent: 0 }),
("922337203685477580800", u64::MAX, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8, 0, 0], fractional: vec![], exponent: 0 }),
("922337203685477580.8", 922_337_203_685_477_581, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0], fractional: vec![8], exponent: 0 }),
("92233720368547758.08", 92_233_720_368_547_759, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8], fractional: vec![0, 8], exponent: 0 }),
("-9223372036854775809", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 9], fractional: vec![], exponent: 0 }),
("-92233720368547758090", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 9, 0], fractional: vec![], exponent: 0 }),
("-922337203685477580900", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 9, 0, 0], fractional: vec![], exponent: 0 }),
("-922337203685477580.9", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0], fractional: vec![9], exponent: 0 }),
("-92233720368547758.09", 0, Quantity { sign: Sign::Negative, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8], fractional: vec![0, 9], exponent: 0 }),
("0.025Ti", 25_000_000_000, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 2, 5], exponent: 12 }),
("1.025Ti", 1_025_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![0, 2, 5], exponent: 12 }),
("-1.025Ti", 0, Quantity { sign: Sign::Negative, integral: vec![1], fractional: vec![0, 2, 5], exponent: 12 }),
(".", 0, Quantity { sign: Sign::Positive, integral: vec![], fractional: vec![], exponent: 0 }),
("-.", 0, Quantity { sign: Sign::Negative, integral: vec![], fractional: vec![], exponent: 0 }),
("1E-3", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: -3 }),
("0", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 0 }),
("0n", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: -9 }),
("0u", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: -6 }),
("0m", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: -3 }),
("0Ki", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 3 }),
("0k", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 3 }),
("0Mi", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 6 }),
("0M", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 6 }),
("0Gi", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 9 }),
("0G", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 9 }),
("0Ti", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 12 }),
("0T", 0, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![], exponent: 12 }),
("1", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 0 }),
("1Ki", 1_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 3 }),
("8Ki", 8_000, Quantity { sign: Sign::Positive, integral: vec![8], fractional: vec![], exponent: 3 }),
("7Mi", 7_000_000, Quantity { sign: Sign::Positive, integral: vec![7], fractional: vec![], exponent: 6 }),
("6Gi", 6_000_000_000, Quantity { sign: Sign::Positive, integral: vec![6], fractional: vec![], exponent: 9 }),
("5Ti", 5_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![], exponent: 12 }),
("4Pi", 4_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![4], fractional: vec![], exponent: 15 }),
("3Ei", 3_000_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![3], fractional: vec![], exponent: 18 }),
("10Ti", 10_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1, 0], fractional: vec![], exponent: 12 }),
("100Ti", 100_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1, 0, 0], fractional: vec![], exponent: 12 }),
("5n", 1, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![], exponent: -9 }),
("4u", 1, Quantity { sign: Sign::Positive, integral: vec![4], fractional: vec![], exponent: -6 }),
("3m", 1, Quantity { sign: Sign::Positive, integral: vec![3], fractional: vec![], exponent: -3 }),
("9", 9, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![], exponent: 0 }),
("8k", 8_000, Quantity { sign: Sign::Positive, integral: vec![8], fractional: vec![], exponent: 3 }),
("50k", 50_000, Quantity { sign: Sign::Positive, integral: vec![5, 0], fractional: vec![], exponent: 3 }),
("7M", 7_000_000, Quantity { sign: Sign::Positive, integral: vec![7], fractional: vec![], exponent: 6 }),
("6G", 6_000_000_000, Quantity { sign: Sign::Positive, integral: vec![6], fractional: vec![], exponent: 9 }),
("5T", 5_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![], exponent: 12 }),
("40T", 40_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![4, 0], fractional: vec![], exponent: 12 }),
("300T", 300_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![3, 0, 0], fractional: vec![], exponent: 12 }),
("2P", 2_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![2], fractional: vec![], exponent: 15 }),
("1E", 1_000_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 18 }),
("1E-3", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: -3 }),
("1e3", 1_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 3 }),
("1E6", 1_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 6 }),
("1e9", 1_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 9 }),
("1E12", 1_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 12 }),
("1e15", 1_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 15 }),
("1E18", 1_000_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 18 }),
("1e14", 100_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 14 }),
("1e13", 10_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 13 }),
("1e15", 1_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 15 }),
("1e3", 1_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 3 }),
("100.035k", 100_035, Quantity { sign: Sign::Positive, integral: vec![1, 0, 0], fractional: vec![0, 3, 5], exponent: 3 }),
("0.001", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 1], exponent: 0 }),
("0.0005k", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 5], exponent: 3 }),
("0.005", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 5], exponent: 0 }),
("0.05", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 5], exponent: 0 }),
("0.5", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], exponent: 0 }),
("0.00050k", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 5, 0], exponent: 3 }),
("0.00500", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 5, 0, 0], exponent: 0 }),
("0.05000", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 5, 0, 0, 0], exponent: 0 }),
("0.50000", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5, 0, 0, 0, 0], exponent: 0 }),
("0.5e0", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], exponent: 0 }),
("0.5e-1", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], exponent: -1 }),
("0.5e-2", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], exponent: -2 }),
("0.5e0", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], exponent: 0 }),
("10.035M", 10_035_000, Quantity { sign: Sign::Positive, integral: vec![1, 0], fractional: vec![0, 3, 5], exponent: 6 }),
("1.2e3", 1_200, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![2], exponent: 3 }),
("1.3E+6", 1_300_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![3], exponent: 6 }),
("1.40e9", 1_400_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![4, 0], exponent: 9 }),
("1.53E12", 1_530_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![5, 3], exponent: 12 }),
("1.6e15", 1_600_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![6], exponent: 15 }),
("1.7E18", 1_700_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![7], exponent: 18 }),
("9.01", 10, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![0, 1], exponent: 0 }),
("8.1k", 8_100, Quantity { sign: Sign::Positive, integral: vec![8], fractional: vec![1], exponent: 3 }),
("7.123456M", 7_123_456, Quantity { sign: Sign::Positive, integral: vec![7], fractional: vec![1, 2, 3, 4, 5, 6], exponent: 6 }),
("6.987654321G", 6_987_654_321, Quantity { sign: Sign::Positive, integral: vec![6], fractional: vec![9, 8, 7, 6, 5, 4, 3, 2, 1], exponent: 9 }),
("5.444T", 5_444_000_000_000, Quantity { sign: Sign::Positive, integral: vec![5], fractional: vec![4, 4, 4], exponent: 12 }),
("40.1T", 40_100_000_000_000, Quantity { sign: Sign::Positive, integral: vec![4, 0], fractional: vec![1], exponent: 12 }),
("300.2T", 300_200_000_000_000, Quantity { sign: Sign::Positive, integral: vec![3, 0, 0], fractional: vec![2], exponent: 12 }),
("2.5P", 2_500_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![2], fractional: vec![5], exponent: 15 }),
("1.01E", 1_010_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![0, 1], exponent: 18 }),
("3.001n", 1, Quantity { sign: Sign::Positive, integral: vec![3], fractional: vec![0, 0, 1], exponent: -9 }),
("1.1E-9", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![1], exponent: -9 }),
("0.0000000001", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 1], exponent: 0 }),
("0.0000000005", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 5], exponent: 0 }),
("0.00000000050", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0], exponent: 0 }),
("0.5e-9", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], exponent: -9 }),
("0.9n", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![9], exponent: -9 }),
("0.00000012345", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5], exponent: 0 }),
("0.00000012354", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 1, 2, 3, 5, 4], exponent: 0 }),
("9Ei", 9_000_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![], exponent: 18 }),
("9223372036854775807Ki", u64::MAX, Quantity { sign: Sign::Positive, integral: vec![9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 7], fractional: vec![], exponent: 3 }),
("12E", 12_000_000_000_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1, 2], fractional: vec![], exponent: 18 }),
("100.035Ki", 100_035, Quantity { sign: Sign::Positive, integral: vec![1, 0, 0], fractional: vec![0, 3, 5], exponent: 3 }),
("0.5Mi", 500_000, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![5], exponent: 6 }),
("0.05Gi", 50_000_000, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 5], exponent: 9 }),
("0.025Ti", 25_000_000_000, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 2, 5], exponent: 12 }),
("0.000000000001Ki", 1, Quantity { sign: Sign::Positive, integral: vec![0], fractional: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], exponent: 3 }),
(".001", 1, Quantity { sign: Sign::Positive, integral: vec![], fractional: vec![0, 0, 1], exponent: 0 }),
(".0001k", 1, Quantity { sign: Sign::Positive, integral: vec![], fractional: vec![0, 0, 0, 1], exponent: 3 }),
("1.", 1, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 0 }),
("1.G", 1_000_000_000, Quantity { sign: Sign::Positive, integral: vec![1], fractional: vec![], exponent: 9 }),
("9.01", 10, Quantity { sign: Sign::Positive, integral: vec![9], fractional: vec![0, 1], exponent: 0 }),
("-9.01", 0, Quantity { sign: Sign::Negative, integral: vec![9], fractional: vec![0, 1], exponent: 0 }),
];
for (input, expected_u64, expected_quantity) in test_cases {
eprintln!("input: {input}");
let actual_quantity: Quantity = input.parse().unwrap();
assert_eq!(expected_quantity, actual_quantity);
let actual_u64: u64 = actual_quantity.into();
assert_eq!(expected_u64, actual_u64);
}
}
// Ref: https://github.com/kubernetes/kubernetes/blob/v1.29.1/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go
#[test]
fn err() {
for (input, expected) in [
("1.1.M", r#"expected suffix, found ".M""#),
("1+1.0M", r#"expected suffix, found "+1.0M""#),
("0.1mi", r#"expected suffix, found "mi""#),
("0.1am", r#"expected suffix, found "am""#),
("aoeu", r#"expected signed number, found "aoeu""#),
(".5i", r#"expected suffix, found "i""#),
("1i", r#"expected suffix, found "i""#),
("-3.01i", r#"expected suffix, found "i""#),
("-3.01e-", "digits is empty"),
(" 1", r#"expected signed number, found " 1""#),
("1 ", r#"expected suffix, found " ""#),
] {
eprintln!("input: {input}");
let actual = input.parse::<Quantity>().unwrap_err();
assert_eq!(expected, actual);
}
}
}
from k8s-openapi.
Related Issues (20)
- VolumeSnapshot and friends? HOT 2
- Consider extending the crate with merge functionality HOT 7
- Documentation snippet about kubernetes detection is not working as expected HOT 4
- Improve Name and Namespace optionality HOT 7
- i32 for port number HOT 5
- `MicroTime` can be `null` HOT 6
- Add `latest` and `earliest` version features that track highest and lowest supported version HOT 1
- Can't find `meta/v1/Duration` HOT 2
- `k8s_openapi::ListResponse` should be serializable HOT 2
- release with v1.27 HOT 3
- v1.28.0
- Looking for example on how to use k8s_openapi::api::core::v1::Event::create function HOT 1
- ServiceSpec not creating headless service when cluster_ip set to None HOT 4
- Size of generated code HOT 7
- Tracking issue for bringing back API operations-related code if there is demand HOT 6
- about k8s-openapi features upgrade question HOT 4
- Comparison of elements is too strict HOT 2
- int-or-string parameters are typed out with mismatched "type" property HOT 2
- Namespaces are not Serializing HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from k8s-openapi.