1use std::num::{
2 NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8,
3 NonZeroUsize,
4};
5
6use num_traits::{Bounded, One, ToPrimitive, Zero};
7use serde::Serialize;
8use serde_json::Number;
9use vector_config_common::num::{NUMERIC_ENFORCED_LOWER_BOUND, NUMERIC_ENFORCED_UPPER_BOUND};
10
11use crate::schema::InstanceType;
12
13#[derive(Clone, Copy, Serialize)]
15pub enum NumberClass {
16 #[serde(rename = "int")]
18 Signed,
19
20 #[serde(rename = "uint")]
22 Unsigned,
23
24 #[serde(rename = "float")]
26 FloatingPoint,
27}
28
29impl NumberClass {
30 pub fn as_instance_type(self) -> InstanceType {
35 match self {
36 Self::Signed | Self::Unsigned => InstanceType::Integer,
37 Self::FloatingPoint => InstanceType::Number,
38 }
39 }
40}
41
42pub trait ConfigurableNumber {
44 type Numeric: Bounded + ToPrimitive + Zero + One;
50
51 fn class() -> NumberClass;
53
54 fn is_nonzero() -> bool {
56 false
57 }
58
59 fn requires_nonzero_exclusion() -> bool {
66 false
67 }
68
69 fn get_encoded_zero_value() -> Number {
71 let zero_num_unsigned = Self::Numeric::zero().to_u64().map(Into::into);
72 let zero_num_floating = Self::Numeric::zero().to_f64().and_then(Number::from_f64);
73 zero_num_unsigned
74 .or(zero_num_floating)
75 .expect("No usable integer type should be unrepresentable by both `u64` and `f64`.")
76 }
77
78 fn get_enforced_min_bound() -> f64 {
80 let mechanical_minimum = match (Self::is_nonzero(), Self::requires_nonzero_exclusion()) {
81 (false, _) | (true, true) => Self::Numeric::min_value(),
85 (true, false) => Self::Numeric::one(),
87 };
88
89 let enforced_minimum = NUMERIC_ENFORCED_LOWER_BOUND;
90 let mechanical_minimum = mechanical_minimum
91 .to_f64()
92 .expect("`Configurable` does not support numbers larger than an `f64` representation");
93
94 if mechanical_minimum < enforced_minimum {
95 enforced_minimum
96 } else {
97 mechanical_minimum
98 }
99 }
100
101 fn get_enforced_max_bound() -> f64 {
103 let enforced_maximum = NUMERIC_ENFORCED_UPPER_BOUND;
104 let mechanical_maximum = Self::Numeric::max_value()
105 .to_f64()
106 .expect("`Configurable` does not support numbers larger than an `f64` representation");
107
108 if mechanical_maximum > enforced_maximum {
109 enforced_maximum
110 } else {
111 mechanical_maximum
112 }
113 }
114}
115
116macro_rules! impl_configurable_number {
117 ([$class:expr] $($ty:ty),+) => {
118 $(
119 impl ConfigurableNumber for $ty {
120 type Numeric = $ty;
121
122 fn class() -> NumberClass {
123 $class
124 }
125 }
126 )+
127 };
128}
129
130macro_rules! impl_configurable_number_nonzero {
131 ([$class:expr] $($aty:ty => $ity:ty),+) => {
132 $(
133 impl ConfigurableNumber for $aty {
134 type Numeric = $ity;
135
136 fn is_nonzero() -> bool {
137 true
138 }
139
140 fn class() -> NumberClass {
141 $class
142 }
143 }
144 )+
145 };
146
147 (with_exclusion, [$class:expr] $($aty:ty => $ity:ty),+) => {
148 $(
149 impl ConfigurableNumber for $aty {
150 type Numeric = $ity;
151
152 fn is_nonzero() -> bool {
153 true
154 }
155
156 fn requires_nonzero_exclusion() -> bool {
157 true
158 }
159
160 fn class() -> NumberClass {
161 $class
162 }
163 }
164 )+
165 };
166}
167
168impl_configurable_number!([NumberClass::Unsigned] u8, u16, u32, u64, usize);
169impl_configurable_number!([NumberClass::Signed] i8, i16, i32, i64, isize);
170impl_configurable_number!([NumberClass::FloatingPoint] f32, f64);
171impl_configurable_number_nonzero!([NumberClass::Unsigned] NonZeroU8 => u8, NonZeroU16 => u16, NonZeroU32 => u32, NonZeroU64 => u64, NonZeroUsize => usize);
172impl_configurable_number_nonzero!(with_exclusion, [NumberClass::Signed] NonZeroI8 => i8, NonZeroI16 => i16, NonZeroI32 => i32, NonZeroI64 => i64);