11use std:: {
22 fmt:: { Display , Write } ,
33 num:: ParseFloatError ,
4- ops:: Deref ,
54 str:: FromStr ,
65} ;
76
@@ -12,9 +11,11 @@ mod cpu;
1211mod macros;
1312mod memory;
1413mod ops;
14+ mod suffix;
1515
1616pub use cpu:: * ;
1717pub use memory:: * ;
18+ pub use suffix:: * ;
1819
1920#[ derive( Debug , PartialEq , Snafu ) ]
2021pub enum ParseQuantityError {
@@ -123,8 +124,7 @@ impl TryFrom<&K8sQuantity> for Quantity {
123124impl Quantity {
124125 /// Optionally scales up or down to the provided `suffix`.
125126 ///
126- /// This function returns a value pair which contains an optional [`Quantity`] and a bool
127- /// indicating if the function performed any scaling. It returns `false` in the following cases:
127+ /// No scaling is performed in the following cases:
128128 ///
129129 /// - the suffixes already match
130130 /// - the value is 0
@@ -144,250 +144,11 @@ impl Quantity {
144144 }
145145}
146146
147- #[ derive( Debug , PartialEq , Snafu ) ]
148- #[ snafu( display( "failed to parse {input:?} as quantity suffix" ) ) ]
149- pub struct ParseSuffixError {
150- input : String ,
151- }
152-
153- #[ derive( Clone , Copy , Debug , PartialEq , PartialOrd ) ]
154- pub enum Suffix {
155- DecimalMultiple ( DecimalMultiple ) ,
156- BinaryMultiple ( BinaryMultiple ) ,
157- DecimalExponent ( DecimalExponent ) ,
158- }
159-
160- impl FromStr for Suffix {
161- type Err = ParseSuffixError ;
162-
163- fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
164- if let Ok ( binary_si) = BinaryMultiple :: from_str ( input) {
165- return Ok ( Self :: BinaryMultiple ( binary_si) ) ;
166- }
167-
168- if let Ok ( decimal_si) = DecimalMultiple :: from_str ( input) {
169- return Ok ( Self :: DecimalMultiple ( decimal_si) ) ;
170- }
171-
172- if input. starts_with ( [ 'e' , 'E' ] ) {
173- if let Ok ( decimal_exponent) = f64:: from_str ( & input[ 1 ..] ) {
174- return Ok ( Self :: DecimalExponent ( DecimalExponent ( decimal_exponent) ) ) ;
175- }
176- }
177-
178- ParseSuffixSnafu { input } . fail ( )
179- }
180- }
181-
182- impl Display for Suffix {
183- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
184- match self {
185- Suffix :: DecimalMultiple ( decimal) => write ! ( f, "{decimal}" ) ,
186- Suffix :: BinaryMultiple ( binary) => write ! ( f, "{binary}" ) ,
187- Suffix :: DecimalExponent ( float) => write ! ( f, "e{float}" ) ,
188- }
189- }
190- }
191-
192- impl Suffix {
193- pub fn factor ( & self ) -> f64 {
194- match self {
195- Suffix :: DecimalMultiple ( s) => s. factor ( ) ,
196- Suffix :: BinaryMultiple ( s) => s. factor ( ) ,
197- Suffix :: DecimalExponent ( s) => s. factor ( ) ,
198- }
199- }
200- }
201-
202- /// Supported byte-multiples based on powers of 2.
203- ///
204- /// These units are defined in IEC 80000-13 and are supported by other standards bodies like NIST.
205- /// The following list contains examples using the official units which Kubernetes adopted with
206- /// slight changes (mentioned in parentheses).
207- ///
208- /// ```plain
209- /// - 1024^1, KiB (Ki), Kibibyte
210- /// - 1024^2, MiB (Mi), Mebibyte
211- /// - 1024^3, GiB (Gi), Gibibyte
212- /// - 1024^4, TiB (Ti), Tebibyte
213- /// - 1024^5, PiB (Pi), Pebibyte
214- /// - 1024^6, EiB (Ei), Exbibyte
215- /// ```
216- ///
217- /// All units bigger than Exbibyte are not a valid suffix according to the [Kubernetes serialization
218- /// format][k8s-serialization-format].
219- ///
220- /// ### See
221- ///
222- /// - <https://en.wikipedia.org/wiki/Byte#Multiple-byte_units>
223- /// - <https://physics.nist.gov/cuu/Units/binary.html>
224- ///
225- /// [k8s-serialization-format]: https://github.com/kubernetes/apimachinery/blob/8c60292e48e46c4faa1e92acb232ce6adb37512c/pkg/api/resource/quantity.go#L37-L59
226- #[ derive( Clone , Copy , Debug , PartialEq , PartialOrd , strum:: Display , strum:: EnumString ) ]
227- pub enum BinaryMultiple {
228- #[ strum( serialize = "Ki" ) ]
229- Kibi ,
230-
231- #[ strum( serialize = "Mi" ) ]
232- Mebi ,
233-
234- #[ strum( serialize = "Gi" ) ]
235- Gibi ,
236-
237- #[ strum( serialize = "Ti" ) ]
238- Tebi ,
239-
240- #[ strum( serialize = "Pi" ) ]
241- Pebi ,
242-
243- #[ strum( serialize = "Ei" ) ]
244- Exbi ,
245- }
246-
247- impl BinaryMultiple {
248- /// Returns the factor based on powers of 2.
249- pub fn factor ( & self ) -> f64 {
250- match self {
251- BinaryMultiple :: Kibi => 2f64 . powi ( 10 ) ,
252- BinaryMultiple :: Mebi => 2f64 . powi ( 20 ) ,
253- BinaryMultiple :: Gibi => 2f64 . powi ( 30 ) ,
254- BinaryMultiple :: Tebi => 2f64 . powi ( 40 ) ,
255- BinaryMultiple :: Pebi => 2f64 . powi ( 50 ) ,
256- BinaryMultiple :: Exbi => 2f64 . powi ( 60 ) ,
257- }
258- }
259- }
260-
261- /// Supported byte-multiples based on powers of 10.
262- ///
263- /// These units are recommended by the International Electrotechnical Commission (IEC). The
264- /// following list contains examples using the official SI units and the units used by Kubernetes
265- /// (mentioned in parentheses). Units used by Kubernetes are a shortened version of the SI units.
266- ///
267- /// It should also be noted that there is an inconsistency in the format Kubernetes uses. Kilobytes
268- /// should use 'K' instead of 'k'.
269- ///
270- /// ```plain
271- /// - 1000^-1, (m): millibyte (Kubernetes only)
272- /// - 1000^ 0, B ( ): byte (no suffix)
273- /// - 1000^ 1, kB (k): kilobyte
274- /// - 1000^ 2, MB (M): Megabyte
275- /// - 1000^ 3, GB (G): Gigabyte
276- /// - 1000^ 4, TB (T): Terabyte
277- /// - 1000^ 5, PB (P): Petabyte
278- /// - 1000^ 6, EB (E): Exabyte
279- /// ```
280- ///
281- /// All units bigger than Exabyte are not a valid suffix according to the [Kubernetes serialization
282- /// format][k8s-serialization-format].
283- ///
284- /// ### See
285- ///
286- /// - <https://en.wikipedia.org/wiki/Byte#Multiple-byte_units>
287- /// - <https://physics.nist.gov/cuu/Units/binary.html>
288- ///
289- /// [k8s-serialization-format]: https://github.com/kubernetes/apimachinery/blob/8c60292e48e46c4faa1e92acb232ce6adb37512c/pkg/api/resource/quantity.go#L37-L59
290- #[ derive( Clone , Copy , Debug , PartialEq , PartialOrd , strum:: Display , strum:: EnumString ) ]
291- pub enum DecimalMultiple {
292- #[ strum( serialize = "m" ) ]
293- Milli ,
294-
295- #[ strum( serialize = "" ) ]
296- Empty ,
297-
298- #[ strum( serialize = "k" ) ]
299- Kilo ,
300-
301- #[ strum( serialize = "M" ) ]
302- Mega ,
303-
304- #[ strum( serialize = "G" ) ]
305- Giga ,
306-
307- #[ strum( serialize = "T" ) ]
308- Tera ,
309-
310- #[ strum( serialize = "P" ) ]
311- Peta ,
312-
313- #[ strum( serialize = "E" ) ]
314- Exa ,
315- }
316-
317- impl DecimalMultiple {
318- pub fn factor ( & self ) -> f64 {
319- match self {
320- DecimalMultiple :: Milli => 10f64 . powi ( -3 ) ,
321- DecimalMultiple :: Empty => 10f64 . powi ( 0 ) ,
322- DecimalMultiple :: Kilo => 10f64 . powi ( 3 ) ,
323- DecimalMultiple :: Mega => 10f64 . powi ( 6 ) ,
324- DecimalMultiple :: Giga => 10f64 . powi ( 9 ) ,
325- DecimalMultiple :: Tera => 10f64 . powi ( 12 ) ,
326- DecimalMultiple :: Peta => 10f64 . powi ( 15 ) ,
327- DecimalMultiple :: Exa => 10f64 . powi ( 18 ) ,
328- }
329- }
330- }
331-
332- /// Scientific (also known as E) notation of numbers.
333- ///
334- /// ### See
335- ///
336- /// - <https://en.wikipedia.org/wiki/Scientific_notation#E_notation>
337- #[ derive( Clone , Copy , Debug , PartialEq , PartialOrd ) ]
338- pub struct DecimalExponent ( f64 ) ;
339-
340- impl Deref for DecimalExponent {
341- type Target = f64 ;
342-
343- fn deref ( & self ) -> & Self :: Target {
344- & self . 0
345- }
346- }
347-
348- impl Display for DecimalExponent {
349- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
350- write ! ( f, "{}" , self . 0 )
351- }
352- }
353-
354- impl DecimalExponent {
355- pub fn factor ( & self ) -> f64 {
356- 10f64 . powf ( self . 0 )
357- }
358- }
359-
360147#[ cfg( test) ]
361148mod test {
362149 use super :: * ;
363150 use rstest:: rstest;
364151
365- #[ rstest]
366- #[ case( "Ki" , Suffix :: BinaryMultiple ( BinaryMultiple :: Kibi ) ) ]
367- #[ case( "Mi" , Suffix :: BinaryMultiple ( BinaryMultiple :: Mebi ) ) ]
368- #[ case( "Gi" , Suffix :: BinaryMultiple ( BinaryMultiple :: Gibi ) ) ]
369- #[ case( "Ti" , Suffix :: BinaryMultiple ( BinaryMultiple :: Tebi ) ) ]
370- #[ case( "Pi" , Suffix :: BinaryMultiple ( BinaryMultiple :: Pebi ) ) ]
371- #[ case( "Ei" , Suffix :: BinaryMultiple ( BinaryMultiple :: Exbi ) ) ]
372- fn binary_multiple_from_str_pass ( #[ case] input : & str , #[ case] expected : Suffix ) {
373- let parsed = Suffix :: from_str ( input) . unwrap ( ) ;
374- assert_eq ! ( parsed, expected) ;
375- }
376-
377- #[ rstest]
378- #[ case( "m" , Suffix :: DecimalMultiple ( DecimalMultiple :: Milli ) ) ]
379- #[ case( "" , Suffix :: DecimalMultiple ( DecimalMultiple :: Empty ) ) ]
380- #[ case( "k" , Suffix :: DecimalMultiple ( DecimalMultiple :: Kilo ) ) ]
381- #[ case( "M" , Suffix :: DecimalMultiple ( DecimalMultiple :: Mega ) ) ]
382- #[ case( "G" , Suffix :: DecimalMultiple ( DecimalMultiple :: Giga ) ) ]
383- #[ case( "T" , Suffix :: DecimalMultiple ( DecimalMultiple :: Tera ) ) ]
384- #[ case( "P" , Suffix :: DecimalMultiple ( DecimalMultiple :: Peta ) ) ]
385- #[ case( "E" , Suffix :: DecimalMultiple ( DecimalMultiple :: Exa ) ) ]
386- fn decimal_multiple_from_str_pass ( #[ case] input : & str , #[ case] expected : Suffix ) {
387- let parsed = Suffix :: from_str ( input) . unwrap ( ) ;
388- assert_eq ! ( parsed, expected) ;
389- }
390-
391152 #[ rstest]
392153 #[ case( "49041204Ki" , Quantity { value: 49041204.0 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Kibi ) } ) ]
393154 #[ case( "256Ki" , Quantity { value: 256.0 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Kibi ) } ) ]
@@ -416,10 +177,10 @@ mod test {
416177 }
417178
418179 #[ rstest]
419- #[ case( "1.234e-3.21" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent ( -3.21 ) ) } ) ]
420- #[ case( "1.234E-3.21" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent ( -3.21 ) ) } ) ]
421- #[ case( "1.234e3" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent ( 3.0 ) ) } ) ]
422- #[ case( "1.234E3" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent ( 3.0 ) ) } ) ]
180+ #[ case( "1.234e-3.21" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from ( -3.21 ) ) } ) ]
181+ #[ case( "1.234E-3.21" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from ( -3.21 ) ) } ) ]
182+ #[ case( "1.234e3" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from ( 3.0 ) ) } ) ]
183+ #[ case( "1.234E3" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from ( 3.0 ) ) } ) ]
423184 fn decimal_exponent_quantity_from_str_pass ( #[ case] input : & str , #[ case] expected : Quantity ) {
424185 let parsed = Quantity :: from_str ( input) . unwrap ( ) ;
425186 assert_eq ! ( parsed, expected) ;
@@ -440,14 +201,13 @@ mod test {
440201 }
441202
442203 #[ rstest]
443- #[ case( "1Mi" , BinaryMultiple :: Kibi , "1024Ki" , true ) ]
444- #[ case( "1024Ki" , BinaryMultiple :: Mebi , "1Mi" , true ) ]
445- #[ case( "1Mi" , BinaryMultiple :: Mebi , "1Mi" , false ) ]
204+ #[ case( "1Mi" , BinaryMultiple :: Kibi , "1024Ki" ) ]
205+ #[ case( "1024Ki" , BinaryMultiple :: Mebi , "1Mi" ) ]
206+ #[ case( "1Mi" , BinaryMultiple :: Mebi , "1Mi" ) ]
446207 fn binary_to_binary_scale_pass (
447208 #[ case] input : & str ,
448209 #[ case] scale_to : BinaryMultiple ,
449210 #[ case] output : & str ,
450- #[ case] _scaled : bool ,
451211 ) {
452212 let parsed = Quantity :: from_str ( input)
453213 . unwrap ( )
@@ -457,13 +217,12 @@ mod test {
457217 }
458218
459219 #[ rstest]
460- #[ case( "1Mi" , DecimalMultiple :: Kilo , "1048.576k" , true ) ]
461- #[ case( "1Mi" , DecimalMultiple :: Mega , "1.048576M" , true ) ]
220+ #[ case( "1Mi" , DecimalMultiple :: Kilo , "1048.576k" ) ]
221+ #[ case( "1Mi" , DecimalMultiple :: Mega , "1.048576M" ) ]
462222 fn binary_to_decimal_scale_pass (
463223 #[ case] input : & str ,
464224 #[ case] scale_to : DecimalMultiple ,
465225 #[ case] output : & str ,
466- #[ case] _scaled : bool ,
467226 ) {
468227 let parsed = Quantity :: from_str ( input)
469228 . unwrap ( )
@@ -473,14 +232,13 @@ mod test {
473232 }
474233
475234 #[ rstest]
476- #[ case( "1M" , DecimalMultiple :: Kilo , "1000k" , true ) ]
477- #[ case( "1000k" , DecimalMultiple :: Mega , "1M" , true ) ]
478- #[ case( "1M" , DecimalMultiple :: Mega , "1M" , false ) ]
235+ #[ case( "1M" , DecimalMultiple :: Kilo , "1000k" ) ]
236+ #[ case( "1000k" , DecimalMultiple :: Mega , "1M" ) ]
237+ #[ case( "1M" , DecimalMultiple :: Mega , "1M" ) ]
479238 fn decimal_to_decimal_scale_pass (
480239 #[ case] input : & str ,
481240 #[ case] scale_to : DecimalMultiple ,
482241 #[ case] output : & str ,
483- #[ case] _scaled : bool ,
484242 ) {
485243 let parsed = Quantity :: from_str ( input)
486244 . unwrap ( )
@@ -490,14 +248,13 @@ mod test {
490248 }
491249
492250 #[ rstest]
493- #[ case( "1e3" , DecimalExponent ( 0.0 ) , "1000e0" , true ) ]
494- #[ case( "1000e0" , DecimalExponent ( 3.0 ) , "1e3" , true ) ]
495- #[ case( "1e3" , DecimalExponent ( 3.0 ) , "1e3" , false ) ]
251+ #[ case( "1e3" , DecimalExponent :: from ( 0.0 ) , "1000e0" ) ]
252+ #[ case( "1000e0" , DecimalExponent :: from ( 3.0 ) , "1e3" ) ]
253+ #[ case( "1e3" , DecimalExponent :: from ( 3.0 ) , "1e3" ) ]
496254 fn decimal_exponent_to_decimal_exponent_scale_pass (
497255 #[ case] input : & str ,
498256 #[ case] scale_to : DecimalExponent ,
499257 #[ case] output : & str ,
500- #[ case] _scaled : bool ,
501258 ) {
502259 let parsed = Quantity :: from_str ( input)
503260 . unwrap ( )
0 commit comments