-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
apimachinery::pkg::api::resource::Quantity
compatibility with Go
#135
Comments
Ref:
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 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.
Compatibility is not a concern. Almost every release of this crate is already semver-major. |
$dayjob needed code for the specific purpose of deserializing a 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);
}
}
} |
The type in question in Go is significantly more complex than just string. It might be beneficial to mimic its Go behavior in this library as well. Perhaps adding various methods similar to its Go counterpart. 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.
The text was updated successfully, but these errors were encountered: