Skip to content

Commit 0760882

Browse files
committed
wip: Switch device
1 parent 845c022 commit 0760882

File tree

3 files changed

+210
-140
lines changed

3 files changed

+210
-140
lines changed

crates/stackable-operator/src/quantity/cpu.rs

+60-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,71 @@
11
use std::{
2+
fmt::Display,
23
iter::Sum,
34
ops::{Add, Deref},
5+
str::FromStr,
46
};
57

68
use k8s_openapi::apimachinery::pkg::api::resource::Quantity as K8sQuantity;
9+
use snafu::Snafu;
710

8-
use crate::quantity::{macros::forward_quantity_impls, DecimalMultiple, Quantity, Suffix};
11+
use crate::quantity::{
12+
macros::forward_quantity_impls, DecimalExponent, DecimalMultiple, Quantity, Suffix,
13+
};
14+
15+
#[derive(Debug, Snafu)]
16+
pub struct ParseSuffixError {
17+
input: String,
18+
}
19+
20+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
21+
pub enum CpuSuffix {
22+
DecimalMultiple(DecimalMultiple),
23+
DecimalExponent(DecimalExponent),
24+
}
25+
26+
impl FromStr for CpuSuffix {
27+
type Err = ParseSuffixError;
28+
29+
fn from_str(input: &str) -> Result<Self, Self::Err> {
30+
if let Ok(decimal_multiple) = DecimalMultiple::from_str(input) {
31+
return Ok(Self::DecimalMultiple(decimal_multiple));
32+
}
33+
34+
if input.starts_with(['e', 'E']) {
35+
if let Ok(decimal_exponent) = f64::from_str(&input[1..]) {
36+
return Ok(Self::DecimalExponent(DecimalExponent::from(
37+
decimal_exponent,
38+
)));
39+
}
40+
}
41+
42+
ParseSuffixSnafu { input }.fail()
43+
}
44+
}
45+
46+
impl Display for CpuSuffix {
47+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48+
todo!()
49+
}
50+
}
51+
52+
impl Default for CpuSuffix {
53+
fn default() -> Self {
54+
CpuSuffix::DecimalMultiple(DecimalMultiple::Empty)
55+
}
56+
}
57+
58+
impl Suffix for CpuSuffix {
59+
fn factor(&self) -> f64 {
60+
todo!()
61+
}
62+
}
963

1064
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
11-
pub struct CpuQuantity(Quantity);
65+
pub struct CpuQuantity(Quantity<CpuSuffix>);
1266

1367
impl Deref for CpuQuantity {
14-
type Target = Quantity;
68+
type Target = Quantity<CpuSuffix>;
1569

1670
fn deref(&self) -> &Self::Target {
1771
&self.0
@@ -29,7 +83,7 @@ impl Sum for CpuQuantity {
2983
iter.fold(
3084
CpuQuantity(Quantity {
3185
value: 0.0,
32-
suffix: Suffix::DecimalMultiple(DecimalMultiple::Empty),
86+
suffix: CpuSuffix::DecimalMultiple(DecimalMultiple::Empty),
3387
}),
3488
CpuQuantity::add,
3589
)
@@ -41,12 +95,12 @@ forward_quantity_impls!(CpuQuantity, K8sQuantity, usize, f32, f64);
4195
impl CpuQuantity {
4296
pub fn from_millis(value: u32) -> Self {
4397
CpuQuantity(Quantity {
44-
suffix: Suffix::DecimalMultiple(DecimalMultiple::Milli),
98+
suffix: CpuSuffix::DecimalMultiple(DecimalMultiple::Milli),
4599
value: value.into(),
46100
})
47101
}
48102

49-
pub fn scale_to(self, suffix: Suffix) -> Self {
103+
pub fn scale_to(self, suffix: CpuSuffix) -> Self {
50104
Self(self.0.scale_to(suffix))
51105
}
52106
}

crates/stackable-operator/src/quantity/mod.rs

+119-48
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,34 @@ pub use memory::*;
2020
pub use suffix::*;
2121

2222
#[derive(Debug, PartialEq, Snafu)]
23-
pub enum ParseQuantityError {
23+
pub enum ParseQuantityError<E>
24+
where
25+
E: std::error::Error + 'static,
26+
{
2427
#[snafu(display("input is either empty or contains non-ascii characters"))]
2528
InvalidFormat,
2629

2730
#[snafu(display("failed to parse floating point number"))]
2831
InvalidFloat { source: ParseFloatError },
2932

3033
#[snafu(display("failed to parse suffix"))]
31-
InvalidSuffix { source: ParseSuffixError },
34+
InvalidSuffix { source: E },
3235
}
3336

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+
3451
/// Quantity is a representation of a number with a suffix / format.
3552
///
3653
/// This type makes it possible to parse Kubernetes quantity strings like '12Ki', '2M, '1.5e2', or
@@ -95,7 +112,10 @@ pub enum ParseQuantityError {
95112
/// [quantity-format]: https://github.com/kubernetes/apimachinery/blob/3e8e52d6a1259ada73f63c1c7d1fad39d4ba9fb4/pkg/api/resource/quantity.go#L39-L59
96113
/// [sci-notation]: https://en.wikipedia.org/wiki/Scientific_notation#E_notation
97114
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
98-
pub struct Quantity {
115+
pub struct Quantity<S>
116+
where
117+
S: Suffix,
118+
{
99119
// FIXME (@Techassi): Support arbitrary-precision numbers
100120
/// The numeric value of the quantity.
101121
///
@@ -107,18 +127,21 @@ pub struct Quantity {
107127
/// The suffix of the quantity.
108128
///
109129
/// This field holds data parsed from `<suffix>` according to the spec.
110-
suffix: Suffix,
130+
suffix: S,
111131
}
112132

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>;
115138

116139
fn from_str(input: &str) -> Result<Self, Self::Err> {
117140
ensure!(!input.is_empty() && input.is_ascii(), InvalidFormatSnafu);
118141

119142
if input == "0" {
120143
return Ok(Self {
121-
suffix: Suffix::DecimalMultiple(DecimalMultiple::Empty),
144+
suffix: S::default(),
122145
value: 0.0,
123146
});
124147
}
@@ -127,23 +150,26 @@ impl FromStr for Quantity {
127150
Some(suffix_index) => {
128151
let parts = input.split_at(suffix_index);
129152
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)?;
131154

132155
Ok(Self { suffix, value })
133156
}
134157
None => {
135158
let value = f64::from_str(input).context(InvalidFloatSnafu)?;
136159

137160
Ok(Self {
138-
suffix: Suffix::DecimalMultiple(DecimalMultiple::Empty),
161+
suffix: S::default(),
139162
value,
140163
})
141164
}
142165
}
143166
}
144167
}
145168

146-
impl Display for Quantity {
169+
impl<S> Display for Quantity<S>
170+
where
171+
S: Suffix,
172+
{
147173
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148174
if self.value == 0.0 {
149175
return f.write_char('0');
@@ -158,42 +184,57 @@ impl Display for Quantity {
158184
}
159185
}
160186

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 {
163192
K8sQuantity(value.to_string())
164193
}
165194
}
166195

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 {
169201
K8sQuantity(value.to_string())
170202
}
171203
}
172204

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>;
175210

176211
fn try_from(value: K8sQuantity) -> Result<Self, Self::Error> {
177212
Quantity::from_str(&value.0)
178213
}
179214
}
180215

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>;
183221

184222
fn try_from(value: &K8sQuantity) -> Result<Self, Self::Error> {
185223
Quantity::from_str(&value.0)
186224
}
187225
}
188226

189-
impl Quantity {
227+
impl<S> Quantity<S>
228+
where
229+
S: Suffix,
230+
{
190231
/// Optionally scales up or down to the provided `suffix`.
191232
///
192233
/// No scaling is performed in the following cases:
193234
///
194235
/// - the suffixes already match
195236
/// - the value is 0
196-
pub fn scale_to(self, suffix: Suffix) -> Self {
237+
pub fn scale_to(self, suffix: S) -> Self {
197238
match (self.value, &self.suffix) {
198239
(0.0, _) => self,
199240
(_, s) if *s == suffix => self,
@@ -208,6 +249,28 @@ impl Quantity {
208249
}
209250
}
210251

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+
211274
/// Either sets the suffix of `self` to `rhs` or scales `rhs` if `self` has a value other than
212275
/// zero.
213276
///
@@ -228,46 +291,54 @@ impl Quantity {
228291
}
229292
}
230293

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+
231302
#[cfg(test)]
232303
mod test {
233304
use super::*;
234305
use rstest::rstest;
235306

307+
// See https://github.com/kubernetes/apimachinery/blob/3e8e52d6a1259ada73f63c1c7d1fad39d4ba9fb4/pkg/api/resource/quantity_test.go#L276-L287
308+
#[rustfmt::skip]
236309
#[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) {
245327
let parsed = Quantity::from_str(input).unwrap();
246-
assert_eq!(parsed, expected);
247-
}
248328

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);
261331
}
262332

333+
// See https://github.com/kubernetes/apimachinery/blob/3e8e52d6a1259ada73f63c1c7d1fad39d4ba9fb4/pkg/api/resource/quantity_test.go#L289
334+
#[rustfmt::skip]
263335
#[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) {
269340
let parsed = Quantity::from_str(input).unwrap();
270-
assert_eq!(parsed, expected);
341+
assert_eq!(parsed.value, expected_value);
271342
}
272343

273344
#[rstest]

0 commit comments

Comments
 (0)