@@ -20,17 +20,34 @@ pub use memory::*;
20
20
pub use suffix:: * ;
21
21
22
22
#[ derive( Debug , PartialEq , Snafu ) ]
23
- pub enum ParseQuantityError {
23
+ pub enum ParseQuantityError < E >
24
+ where
25
+ E : std:: error:: Error + ' static ,
26
+ {
24
27
#[ snafu( display( "input is either empty or contains non-ascii characters" ) ) ]
25
28
InvalidFormat ,
26
29
27
30
#[ snafu( display( "failed to parse floating point number" ) ) ]
28
31
InvalidFloat { source : ParseFloatError } ,
29
32
30
33
#[ snafu( display( "failed to parse suffix" ) ) ]
31
- InvalidSuffix { source : ParseSuffixError } ,
34
+ InvalidSuffix { source : E } ,
32
35
}
33
36
37
+ // pub struct CpuQuant(Quantity1<DecimalMultiple>);
38
+
39
+ // pub struct Quantity1<T>
40
+ // where
41
+ // T: SuffixTrait,
42
+ // {
43
+ // value: f64,
44
+ // suffix: T,
45
+ // }
46
+
47
+ // pub trait SuffixTrait: FromStr + Default {
48
+ // fn factor(&self) -> f64;
49
+ // }
50
+
34
51
/// Quantity is a representation of a number with a suffix / format.
35
52
///
36
53
/// This type makes it possible to parse Kubernetes quantity strings like '12Ki', '2M, '1.5e2', or
@@ -95,7 +112,10 @@ pub enum ParseQuantityError {
95
112
/// [quantity-format]: https://github.com/kubernetes/apimachinery/blob/3e8e52d6a1259ada73f63c1c7d1fad39d4ba9fb4/pkg/api/resource/quantity.go#L39-L59
96
113
/// [sci-notation]: https://en.wikipedia.org/wiki/Scientific_notation#E_notation
97
114
#[ derive( Clone , Copy , Debug , PartialEq , PartialOrd ) ]
98
- pub struct Quantity {
115
+ pub struct Quantity < S >
116
+ where
117
+ S : Suffix ,
118
+ {
99
119
// FIXME (@Techassi): Support arbitrary-precision numbers
100
120
/// The numeric value of the quantity.
101
121
///
@@ -107,18 +127,21 @@ pub struct Quantity {
107
127
/// The suffix of the quantity.
108
128
///
109
129
/// This field holds data parsed from `<suffix>` according to the spec.
110
- suffix : Suffix ,
130
+ suffix : S ,
111
131
}
112
132
113
- impl FromStr for Quantity {
114
- type Err = ParseQuantityError ;
133
+ impl < S > FromStr for Quantity < S >
134
+ where
135
+ S : Suffix ,
136
+ {
137
+ type Err = ParseQuantityError < S :: Err > ;
115
138
116
139
fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
117
140
ensure ! ( !input. is_empty( ) && input. is_ascii( ) , InvalidFormatSnafu ) ;
118
141
119
142
if input == "0" {
120
143
return Ok ( Self {
121
- suffix : Suffix :: DecimalMultiple ( DecimalMultiple :: Empty ) ,
144
+ suffix : S :: default ( ) ,
122
145
value : 0.0 ,
123
146
} ) ;
124
147
}
@@ -127,23 +150,26 @@ impl FromStr for Quantity {
127
150
Some ( suffix_index) => {
128
151
let parts = input. split_at ( suffix_index) ;
129
152
let value = f64:: from_str ( parts. 0 ) . context ( InvalidFloatSnafu ) ?;
130
- let suffix = Suffix :: from_str ( parts. 1 ) . context ( InvalidSuffixSnafu ) ?;
153
+ let suffix = S :: from_str ( parts. 1 ) . context ( InvalidSuffixSnafu ) ?;
131
154
132
155
Ok ( Self { suffix, value } )
133
156
}
134
157
None => {
135
158
let value = f64:: from_str ( input) . context ( InvalidFloatSnafu ) ?;
136
159
137
160
Ok ( Self {
138
- suffix : Suffix :: DecimalMultiple ( DecimalMultiple :: Empty ) ,
161
+ suffix : S :: default ( ) ,
139
162
value,
140
163
} )
141
164
}
142
165
}
143
166
}
144
167
}
145
168
146
- impl Display for Quantity {
169
+ impl < S > Display for Quantity < S >
170
+ where
171
+ S : Suffix ,
172
+ {
147
173
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
148
174
if self . value == 0.0 {
149
175
return f. write_char ( '0' ) ;
@@ -158,42 +184,57 @@ impl Display for Quantity {
158
184
}
159
185
}
160
186
161
- impl From < Quantity > for K8sQuantity {
162
- fn from ( value : Quantity ) -> Self {
187
+ impl < S > From < Quantity < S > > for K8sQuantity
188
+ where
189
+ S : Suffix ,
190
+ {
191
+ fn from ( value : Quantity < S > ) -> Self {
163
192
K8sQuantity ( value. to_string ( ) )
164
193
}
165
194
}
166
195
167
- impl From < & Quantity > for K8sQuantity {
168
- fn from ( value : & Quantity ) -> Self {
196
+ impl < S > From < & Quantity < S > > for K8sQuantity
197
+ where
198
+ S : Suffix ,
199
+ {
200
+ fn from ( value : & Quantity < S > ) -> Self {
169
201
K8sQuantity ( value. to_string ( ) )
170
202
}
171
203
}
172
204
173
- impl TryFrom < K8sQuantity > for Quantity {
174
- type Error = ParseQuantityError ;
205
+ impl < S > TryFrom < K8sQuantity > for Quantity < S >
206
+ where
207
+ S : Suffix ,
208
+ {
209
+ type Error = ParseQuantityError < S :: Err > ;
175
210
176
211
fn try_from ( value : K8sQuantity ) -> Result < Self , Self :: Error > {
177
212
Quantity :: from_str ( & value. 0 )
178
213
}
179
214
}
180
215
181
- impl TryFrom < & K8sQuantity > for Quantity {
182
- type Error = ParseQuantityError ;
216
+ impl < S > TryFrom < & K8sQuantity > for Quantity < S >
217
+ where
218
+ S : Suffix ,
219
+ {
220
+ type Error = ParseQuantityError < S :: Err > ;
183
221
184
222
fn try_from ( value : & K8sQuantity ) -> Result < Self , Self :: Error > {
185
223
Quantity :: from_str ( & value. 0 )
186
224
}
187
225
}
188
226
189
- impl Quantity {
227
+ impl < S > Quantity < S >
228
+ where
229
+ S : Suffix ,
230
+ {
190
231
/// Optionally scales up or down to the provided `suffix`.
191
232
///
192
233
/// No scaling is performed in the following cases:
193
234
///
194
235
/// - the suffixes already match
195
236
/// - the value is 0
196
- pub fn scale_to ( self , suffix : Suffix ) -> Self {
237
+ pub fn scale_to ( self , suffix : S ) -> Self {
197
238
match ( self . value , & self . suffix ) {
198
239
( 0.0 , _) => self ,
199
240
( _, s) if * s == suffix => self ,
@@ -208,6 +249,28 @@ impl Quantity {
208
249
}
209
250
}
210
251
252
+ // pub fn scale_to_non_zero(self) -> Self {
253
+ // if !self.value.between(-1.0, 1.0) {
254
+ // return self;
255
+ // }
256
+
257
+ // let mut this = self;
258
+
259
+ // while let Some(suffix) = this.suffix.scale_down() {
260
+ // this = self.scale_to(suffix);
261
+ // if this.value.between(-1.0, 1.0) {
262
+ // continue;
263
+ // } else {
264
+ // return this;
265
+ // }
266
+ // }
267
+
268
+ // Self {
269
+ // value: 1.0,
270
+ // suffix: this.suffix,
271
+ // }
272
+ // }
273
+
211
274
/// Either sets the suffix of `self` to `rhs` or scales `rhs` if `self` has a value other than
212
275
/// zero.
213
276
///
@@ -228,46 +291,54 @@ impl Quantity {
228
291
}
229
292
}
230
293
294
+ trait FloatExt : PartialOrd + Sized {
295
+ fn between ( self , start : Self , end : Self ) -> bool {
296
+ self > start && self < end
297
+ }
298
+ }
299
+
300
+ impl FloatExt for f64 { }
301
+
231
302
#[ cfg( test) ]
232
303
mod test {
233
304
use super :: * ;
234
305
use rstest:: rstest;
235
306
307
+ // See https://github.com/kubernetes/apimachinery/blob/3e8e52d6a1259ada73f63c1c7d1fad39d4ba9fb4/pkg/api/resource/quantity_test.go#L276-L287
308
+ #[ rustfmt:: skip]
236
309
#[ rstest]
237
- #[ case( "49041204Ki" , Quantity { value: 49041204.0 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Kibi ) } ) ]
238
- #[ case( "256Ki" , Quantity { value: 256.0 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Kibi ) } ) ]
239
- #[ case( "1.5Gi" , Quantity { value: 1.5 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Gibi ) } ) ]
240
- #[ case( "0.8Ti" , Quantity { value: 0.8 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Tebi ) } ) ]
241
- #[ case( "3.2Pi" , Quantity { value: 3.2 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Pebi ) } ) ]
242
- #[ case( "0.2Ei" , Quantity { value: 0.2 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Exbi ) } ) ]
243
- #[ case( "8Mi" , Quantity { value: 8.0 , suffix: Suffix :: BinaryMultiple ( BinaryMultiple :: Mebi ) } ) ]
244
- fn binary_quantity_from_str_pass ( #[ case] input : & str , #[ case] expected : Quantity ) {
310
+ #[ case( "0" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Empty ) ) ]
311
+ #[ case( "0n" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Nano ) ) ]
312
+ #[ case( "0u" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Micro ) ) ]
313
+ #[ case( "0m" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Milli ) ) ]
314
+ #[ case( "0Ki" , 0.0 , Suffix :: BinaryMultiple ( BinaryMultiple :: Kibi ) ) ]
315
+ #[ case( "0k" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Kilo ) ) ]
316
+ #[ case( "0Mi" , 0.0 , Suffix :: BinaryMultiple ( BinaryMultiple :: Mebi ) ) ]
317
+ #[ case( "0M" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Mega ) ) ]
318
+ #[ case( "0Gi" , 0.0 , Suffix :: BinaryMultiple ( BinaryMultiple :: Gibi ) ) ]
319
+ #[ case( "0G" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Giga ) ) ]
320
+ #[ case( "0Ti" , 0.0 , Suffix :: BinaryMultiple ( BinaryMultiple :: Tebi ) ) ]
321
+ #[ case( "0T" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Tera ) ) ]
322
+ #[ case( "0Pi" , 0.0 , Suffix :: BinaryMultiple ( BinaryMultiple :: Pebi ) ) ]
323
+ #[ case( "0P" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Peta ) ) ]
324
+ #[ case( "0Ei" , 0.0 , Suffix :: BinaryMultiple ( BinaryMultiple :: Exbi ) ) ]
325
+ #[ case( "0E" , 0.0 , Suffix :: DecimalMultiple ( DecimalMultiple :: Exa ) ) ]
326
+ fn parse_zero_quantity ( #[ case] input : & str , #[ case] expected_value : f64 , #[ case] expected_suffix : Suffix ) {
245
327
let parsed = Quantity :: from_str ( input) . unwrap ( ) ;
246
- assert_eq ! ( parsed, expected) ;
247
- }
248
328
249
- #[ rstest]
250
- #[ case( "49041204k" , Quantity { value: 49041204.0 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Kilo ) } ) ]
251
- #[ case( "256k" , Quantity { value: 256.0 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Kilo ) } ) ]
252
- #[ case( "1.5G" , Quantity { value: 1.5 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Giga ) } ) ]
253
- #[ case( "0.8T" , Quantity { value: 0.8 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Tera ) } ) ]
254
- #[ case( "3.2P" , Quantity { value: 3.2 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Peta ) } ) ]
255
- #[ case( "0.2E" , Quantity { value: 0.2 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Exa ) } ) ]
256
- #[ case( "4m" , Quantity { value: 4.0 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Milli ) } ) ]
257
- #[ case( "8M" , Quantity { value: 8.0 , suffix: Suffix :: DecimalMultiple ( DecimalMultiple :: Mega ) } ) ]
258
- fn decimal_quantity_from_str_pass ( #[ case] input : & str , #[ case] expected : Quantity ) {
259
- let parsed = Quantity :: from_str ( input) . unwrap ( ) ;
260
- assert_eq ! ( parsed, expected) ;
329
+ assert_eq ! ( parsed. suffix, expected_suffix) ;
330
+ assert_eq ! ( parsed. value, expected_value) ;
261
331
}
262
332
333
+ // See https://github.com/kubernetes/apimachinery/blob/3e8e52d6a1259ada73f63c1c7d1fad39d4ba9fb4/pkg/api/resource/quantity_test.go#L289
334
+ #[ rustfmt:: skip]
263
335
#[ rstest]
264
- #[ case( "1.234e-3.21" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from( -3.21 ) ) } ) ]
265
- #[ case( "1.234E-3.21" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from( -3.21 ) ) } ) ]
266
- #[ case( "1.234e3" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from( 3.0 ) ) } ) ]
267
- #[ case( "1.234E3" , Quantity { value: 1.234 , suffix: Suffix :: DecimalExponent ( DecimalExponent :: from( 3.0 ) ) } ) ]
268
- fn decimal_exponent_quantity_from_str_pass ( #[ case] input : & str , #[ case] expected : Quantity ) {
336
+ #[ case( "12.34" , 12.34 ) ]
337
+ #[ case( "12" , 12.0 ) ]
338
+ #[ case( "1" , 1.0 ) ]
339
+ fn parse_quantity_without_suffix ( #[ case] input : & str , #[ case] expected_value : f64 ) {
269
340
let parsed = Quantity :: from_str ( input) . unwrap ( ) ;
270
- assert_eq ! ( parsed, expected ) ;
341
+ assert_eq ! ( parsed. value , expected_value ) ;
271
342
}
272
343
273
344
#[ rstest]
0 commit comments