1use std::fmt::Debug;
13use std::ops::{RangeBounds, RangeFrom, RangeInclusive};
14use std::time::Duration;
15
16use mz_repr::adt::numeric::Numeric;
17use mz_repr::bytes::ByteSize;
18
19use super::{Value, Var, VarError};
20
21pub static NUMERIC_NON_NEGATIVE: NumericNonNegNonNan = NumericNonNegNonNan;
22
23pub static NON_ZERO_DURATION: NonZeroDuration = NonZeroDuration;
24
25pub static NUMERIC_BOUNDED_0_1_INCLUSIVE: NumericInRange<RangeInclusive<f64>> =
26 NumericInRange(0.0f64..=1.0);
27
28pub static BYTESIZE_AT_LEAST_1MB: ByteSizeInRange<RangeFrom<ByteSize>> =
29 ByteSizeInRange(ByteSize::mb(1)..);
30
31pub static U32_AT_LEAST_1: U32InRange<RangeFrom<u32>> = U32InRange(1..);
32
33#[derive(Debug)]
34pub enum ValueConstraint {
35 ReadOnly,
37 Fixed,
39 Domain(&'static dyn DynDomainConstraint),
41}
42
43impl ValueConstraint {
44 pub fn check_constraint(
45 &self,
46 var: &dyn Var,
47 cur_value: &dyn Value,
48 new_value: &dyn Value,
49 ) -> Result<(), VarError> {
50 match self {
51 ValueConstraint::ReadOnly => return Err(VarError::ReadOnlyParameter(var.name())),
52 ValueConstraint::Fixed => {
53 if cur_value != new_value {
54 return Err(VarError::FixedValueParameter {
55 name: var.name(),
56 value: cur_value.format(),
57 });
58 }
59 }
60 ValueConstraint::Domain(check) => check.check(var, new_value)?,
61 }
62
63 Ok(())
64 }
65}
66
67impl Clone for ValueConstraint {
68 fn clone(&self) -> Self {
69 match self {
70 ValueConstraint::Fixed => ValueConstraint::Fixed,
71 ValueConstraint::ReadOnly => ValueConstraint::ReadOnly,
72 ValueConstraint::Domain(c) => ValueConstraint::Domain(*c),
73 }
74 }
75}
76
77pub trait DynDomainConstraint: Debug + Send + Sync + 'static {
81 fn check(&self, var: &dyn Var, v: &dyn Value) -> Result<(), VarError>;
82}
83
84impl<D> DynDomainConstraint for D
85where
86 D: DomainConstraint + Send + Sync + 'static,
87 D::Value: Value,
88{
89 fn check(&self, var: &dyn Var, v: &dyn Value) -> Result<(), VarError> {
90 let val = v
91 .as_any()
92 .downcast_ref::<D::Value>()
93 .expect("type should match");
94 self.check(var, val)
95 }
96}
97pub trait DomainConstraint: Debug + Send + Sync + 'static {
98 type Value;
99
100 fn check(&self, var: &dyn Var, v: &Self::Value) -> Result<(), VarError>;
101}
102
103#[derive(Debug, Clone, Eq, PartialEq)]
104pub struct NumericNonNegNonNan;
105
106impl DomainConstraint for NumericNonNegNonNan {
107 type Value = Numeric;
108
109 fn check(&self, var: &dyn Var, n: &Numeric) -> Result<(), VarError> {
110 if n.is_nan() || n.is_negative() {
111 Err(VarError::InvalidParameterValue {
112 name: var.name(),
113 invalid_values: vec![n.to_string()],
114 reason: "only supports non-negative, non-NaN numeric values".to_string(),
115 })
116 } else {
117 Ok(())
118 }
119 }
120}
121
122#[derive(Debug, Clone, Eq, PartialEq)]
123pub struct NonZeroDuration;
124
125impl DomainConstraint for NonZeroDuration {
126 type Value = Duration;
127
128 fn check(&self, var: &dyn Var, d: &Duration) -> Result<(), VarError> {
129 if d.is_zero() {
130 Err(VarError::InvalidParameterValue {
131 name: var.name(),
132 invalid_values: vec![format!("{:?}", d)],
133 reason: "only supports non-zero durations".to_string(),
134 })
135 } else {
136 Ok(())
137 }
138 }
139}
140
141#[derive(Debug, Clone, Eq, PartialEq)]
142pub struct NumericInRange<R>(pub R);
143
144impl<R> DomainConstraint for NumericInRange<R>
145where
146 R: RangeBounds<f64> + std::fmt::Debug + Send + Sync + 'static,
147{
148 type Value = Numeric;
149
150 fn check(&self, var: &dyn Var, n: &Numeric) -> Result<(), VarError> {
151 let n: f64 = (*n)
152 .try_into()
153 .map_err(|_e| VarError::InvalidParameterValue {
154 name: var.name(),
155 invalid_values: vec![n.to_string()],
156 reason: format!("only supports values in range {:?}", self.0),
168 })?;
169 if !self.0.contains(&n) {
170 Err(VarError::InvalidParameterValue {
171 name: var.name(),
172 invalid_values: vec![n.to_string()],
173 reason: format!("only supports values in range {:?}", self.0),
174 })
175 } else {
176 Ok(())
177 }
178 }
179}
180
181#[derive(Debug, Clone, Eq, PartialEq)]
182pub struct ByteSizeInRange<R>(pub R);
183
184impl<R> DomainConstraint for ByteSizeInRange<R>
185where
186 R: RangeBounds<ByteSize> + std::fmt::Debug + Send + Sync + 'static,
187{
188 type Value = ByteSize;
189
190 fn check(&self, var: &dyn Var, size: &ByteSize) -> Result<(), VarError> {
191 if self.0.contains(size) {
192 Ok(())
193 } else {
194 Err(VarError::InvalidParameterValue {
195 name: var.name(),
196 invalid_values: vec![size.to_string()],
197 reason: format!("only supports values in range {:?}", self.0),
198 })
199 }
200 }
201}
202
203#[derive(Debug, Clone, Eq, PartialEq)]
204pub struct U32InRange<R>(pub R);
205
206impl<R> DomainConstraint for U32InRange<R>
207where
208 R: RangeBounds<u32> + std::fmt::Debug + Send + Sync + 'static,
209{
210 type Value = u32;
211
212 fn check(&self, var: &dyn Var, n: &u32) -> Result<(), VarError> {
213 if self.0.contains(n) {
214 Ok(())
215 } else {
216 Err(VarError::InvalidParameterValue {
217 name: var.name(),
218 invalid_values: vec![n.to_string()],
219 reason: format!("only supports values in range {:?}", self.0),
220 })
221 }
222 }
223}