|
| 1 | +//! This module contains types and functions to parse and handle Kubernetes quantities. |
| 2 | +
|
1 | 3 | use std::{
|
2 | 4 | fmt::{Display, Write},
|
3 | 5 | num::ParseFloatError,
|
@@ -29,6 +31,69 @@ pub enum ParseQuantityError {
|
29 | 31 | InvalidSuffix { source: ParseSuffixError },
|
30 | 32 | }
|
31 | 33 |
|
| 34 | +/// Quantity is a representation of a number with a suffix / format. |
| 35 | +/// |
| 36 | +/// This type makes it possible to parse Kubernetes quantity strings like '12Ki', '2M, '1.5e2', or |
| 37 | +/// '0'. This is done by storing the parsed data as two separate values: the `value` and the |
| 38 | +/// `suffix`. The parsing is implemented according to the serialization format laid out in the |
| 39 | +/// Kubernetes [source code][quantity-format]. Roughly, the format looks like this: |
| 40 | +/// |
| 41 | +/// ```plain |
| 42 | +/// quantity ::= <signedNumber><suffix> |
| 43 | +/// suffix ::= <binaryMultiple> | <decimalMultiple> | <decimalExponent> |
| 44 | +/// binaryMultiple ::= Ki | Mi | Gi | Ti | Pi | Ei |
| 45 | +/// decimalMultiple ::= m | "" | k | M | G | T | P | E |
| 46 | +/// decimalExponent ::= "e" <signedNumber> | "E" <signedNumber> |
| 47 | +/// ``` |
| 48 | +/// |
| 49 | +/// Generally speaking, this implementation very closely resembles the original upstream Go |
| 50 | +/// implementation of the Kubernetes project. However there are a few differences which boil down |
| 51 | +/// to being easier to use / implement using Rust and safety. These differences in addition to |
| 52 | +/// general notes on the implementation are detailed below: |
| 53 | +/// |
| 54 | +/// #### Suffixes |
| 55 | +/// |
| 56 | +/// It should be noted that the decimal multiple contains `""` (an empty string / no suffix). This |
| 57 | +/// is why one might think that the suffix is optional. Strictly speaking, it is not optional, but |
| 58 | +/// a missing / empty suffix maps to a decimal multiple with a scaling factor of 1000^0. The |
| 59 | +/// following section goes into more detail about the scaling factors. |
| 60 | +/// |
| 61 | +/// Instead of marking the `suffix` field as optional by using [`Option`], it instead maps the empty |
| 62 | +/// suffix to the [`DecimalMultiple::Empty`] variant. This eases implementing safe mathematical (like |
| 63 | +/// scaling up/down, addition or division) operations on [`Quantity`]. |
| 64 | +/// |
| 65 | +/// The [`Suffix`] enum represents the three different supported suffixes. Each suffix uses a |
| 66 | +/// specific base and exponent for it's scaling factor: |
| 67 | +/// |
| 68 | +/// - The [`BinaryMultiple`] uses a base of 2 and exponents of 10s. |
| 69 | +/// - The [`DecimalMultiple`] uses a base of 10 and exponents of 3s. |
| 70 | +/// - The [`DecimalExponent`] uses a base of 10 and exponents defined using the |
| 71 | +/// [scientific notation][sci-notation]. |
| 72 | +/// |
| 73 | +/// #### Mathematical operations |
| 74 | +/// |
| 75 | +/// Similar to to upstream implementation, math operations can change the suffix / format. |
| 76 | +/// Additionally, it is necessary to change the suffix of the right-hand-side in binary operations |
| 77 | +/// before doing the actual operation (like addition). |
| 78 | +/// |
| 79 | +/// - **Example 1:** `0Ki + 1Mi` - In this example, the lhs has the value **0**. The exact suffix is |
| 80 | +/// irrelevant, but note that it might be different from the suffix of the rhs. Since the value is |
| 81 | +/// zero, we can safely update the suffix of the lhs to `Mi` and continue by adding **1** to the |
| 82 | +/// lhs. The final result is then `1Mi`. |
| 83 | +/// - **Example 2:** `1024Ki + 1Mi` - Here, the lhs is not zero, so we cannot safely update the |
| 84 | +/// suffix. Instead, we need to scale the rhs to the appropriate suffix, `Ki` in this example. |
| 85 | +/// Afterwards the addition of both values can be done. The final result is `2048Ki`. If needed, |
| 86 | +/// this can be scaled to `Mi`, resulting in `2Mi` as expected. |
| 87 | +/// |
| 88 | +/// #### Precision |
| 89 | +/// |
| 90 | +/// The upstream implementation uses infinite-precision arithmetic to be able to store very large |
| 91 | +/// values, up to 2^63-1. This implementation **does not** use infinite-precision arithmetic. The |
| 92 | +/// biggest value which can be safely expresses is [`f64::MAX`]. This value is deemed plenty for |
| 93 | +/// now, but there is always the possibility of using infinite-precision implementation as well. |
| 94 | +/// |
| 95 | +/// [quantity-format]: https://github.com/kubernetes/apimachinery/blob/3e8e52d6a1259ada73f63c1c7d1fad39d4ba9fb4/pkg/api/resource/quantity.go#L39-L59 |
| 96 | +/// [sci-notation]: https://en.wikipedia.org/wiki/Scientific_notation#E_notation |
32 | 97 | #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
33 | 98 | pub struct Quantity {
|
34 | 99 | // FIXME (@Techassi): Support arbitrary-precision numbers
|
|
0 commit comments