1use crate::combo_error::ComboErrors;
2use crate::imports::*;
3use std::cmp::Ordering;
4use std::cmp::PartialOrd;
5use std::fmt::Debug;
6use uom::si::Quantity;
7use uom::ConstZero;
8
9pub type ValidationError = anyhow::Error;
10pub type ValidationErrors = ComboErrors<ValidationError>;
11pub type ValidationResults = Result<(), ValidationErrors>;
12
13pub trait Valid: Sized + Default {
15 fn valid() -> Self {
16 Default::default()
17 }
18}
19
20pub trait ObjState {
22 fn is_fake(&self) -> bool {
23 false
24 }
25 fn validate(&self) -> ValidationResults {
26 Ok(())
27 }
28}
29
30pub trait ObjStateConst: ObjState {
31 fn is_real(&self) -> bool;
32 fn is_valid(&self) -> bool;
33 fn real(&self) -> Option<&Self>;
34}
35
36impl<T: ObjState> ObjStateConst for T {
37 fn is_real(&self) -> bool {
38 !self.is_fake()
39 }
40 fn is_valid(&self) -> bool {
41 self.validate().is_ok()
42 }
43 fn real(&self) -> Option<&Self> {
44 if self.is_fake() {
45 return None;
46 }
47 Some(self)
48 }
49}
50
51pub fn validate_slice_real<T>(errors: &mut ValidationErrors, slice: &[T], elem_name: &str)
52where
53 T: ObjState,
54{
55 validate_slice_real_shift(errors, slice, elem_name, 0)
56}
57
58pub fn validate_slice_real_shift<T>(
59 errors: &mut ValidationErrors,
60 slice: &[T],
61 elem_name: &str,
62 idx_shift: isize,
63) where
64 T: ObjState,
65{
66 for (index, val) in slice.iter().enumerate() {
67 if val.is_fake() {
68 errors.push(anyhow!(
69 "{} at index = {} must be real!",
70 elem_name,
71 index as isize + idx_shift
72 ));
73 }
74 if let Err(mut errors_add) = val.validate() {
75 errors_add.add_context(anyhow!(
76 "{} at index = {} must be valid!",
77 elem_name,
78 index as isize + idx_shift
79 ));
80 errors.append(&mut errors_add);
81 }
82 }
83}
84
85pub fn validate_slice_fake<T>(errors: &mut ValidationErrors, slice: &[T], elem_name: &str)
86where
87 T: ObjState,
88{
89 validate_slice_fake_shift(errors, slice, elem_name, 0)
90}
91
92pub fn validate_slice_fake_shift<T>(
93 errors: &mut ValidationErrors,
94 slice: &[T],
95 elem_name: &str,
96 idx_shift: isize,
97) where
98 T: ObjState,
99{
100 for (index, val) in slice.iter().enumerate() {
101 if val.is_real() {
102 errors.push(anyhow!(
103 "{} at index = {} must be fake!",
104 elem_name,
105 index as isize + idx_shift
106 ));
107 }
108 if let Err(mut errors_add) = val.validate() {
109 errors_add.add_context(anyhow!(
110 "{} at index = {} must be valid!",
111 elem_name,
112 index as isize + idx_shift
113 ));
114 errors.append(&mut errors_add);
115 }
116 }
117}
118
119pub fn validate_field_fake<T>(errors: &mut ValidationErrors, field_val: &T, field_name: &str)
120where
121 T: ObjState + Debug,
122{
123 if !field_val.is_fake() || field_val.is_real() {
124 errors.push(anyhow!(
125 "{} = {:?} must be fake and not real!",
126 field_name,
127 field_val
128 ));
129 }
130 if let Err(mut errors_add) = field_val.validate() {
131 errors_add.add_context(anyhow!("{} must be valid!", field_name));
132 errors.append(&mut errors_add);
133 }
134}
135
136pub fn validate_field_real<T>(errors: &mut ValidationErrors, field_val: &T, field_name: &str)
137where
138 T: ObjState + Debug,
139{
140 if !field_val.is_real() || field_val.is_fake() {
141 errors.push(anyhow!(
142 "{} = {:?} must be real and not fake!",
143 field_name,
144 field_val
145 ));
146 }
147 if let Err(mut errors_add) = field_val.validate() {
148 errors_add.add_context(anyhow!("{} must be valid!", field_name));
149 errors.append(&mut errors_add);
150 }
151}
152
153pub fn si_chk_num<D, U>(
155 errors: &mut ValidationErrors,
156 field_val: &Quantity<D, U, f64>,
157 field_name: &str,
158) where
159 D: uom::si::Dimension + ?Sized,
160 U: uom::si::Units<f64> + ?Sized,
161{
162 if field_val.is_nan() {
163 errors.push(anyhow!(
164 "{} = {:?} must be a number!",
165 field_name,
166 field_val
167 ));
168 }
169}
170
171pub fn si_chk_num_fin<D, U>(
173 errors: &mut ValidationErrors,
174 field_val: &uom::si::Quantity<D, U, f64>,
175 field_name: &str,
176) where
177 D: uom::si::Dimension + ?Sized,
178 U: uom::si::Units<f64> + ?Sized,
179{
180 if field_val.is_nan() || field_val.is_infinite() {
181 errors.push(anyhow!(
182 "{} = {:?} must be a finite number!",
183 field_name,
184 field_val
185 ));
186 }
187}
188
189pub fn si_chk_num_gez<T>(errors: &mut ValidationErrors, field_val: &T, field_name: &str)
191where
192 T: Debug + PartialOrd + ConstZero,
193{
194 if let None | Some(Ordering::Less) = field_val.partial_cmp(&T::ZERO) {
195 errors.push(anyhow!(
196 "{} = {:?} must be a positive number!",
197 field_name,
198 field_val
199 ));
200 }
201}
202
203pub fn si_chk_num_gtz<T>(errors: &mut ValidationErrors, field_val: &T, field_name: &str)
205where
206 T: Debug + PartialOrd + ConstZero,
207{
208 if let None | Some(Ordering::Less) | Some(Ordering::Equal) = field_val.partial_cmp(&T::ZERO) {
209 errors.push(anyhow!(
210 "{} = {:?} must be a number larger than zero!",
211 field_name,
212 field_val
213 ));
214 }
215}
216
217pub fn si_chk_num_gez_fin<D, U>(
219 errors: &mut ValidationErrors,
220 field_val: &uom::si::Quantity<D, U, f64>,
221 field_name: &str,
222) where
223 D: uom::si::Dimension + ?Sized,
224 U: uom::si::Units<f64> + ?Sized,
225{
226 if !(*field_val >= uom::si::Quantity::<D, U, f64>::ZERO && field_val.is_finite()) {
227 errors.push(anyhow!(
228 "{} = {:?} must be a finite positive number!",
229 field_name,
230 field_val
231 ));
232 }
233}
234
235pub fn si_chk_num_gtz_fin<D, U>(
237 errors: &mut ValidationErrors,
238 field_val: &uom::si::Quantity<D, U, f64>,
239 field_name: &str,
240) where
241 D: uom::si::Dimension + ?Sized,
242 U: uom::si::Units<f64> + ?Sized,
243{
244 if !(*field_val > uom::si::Quantity::<D, U, f64>::ZERO && field_val.is_finite()) {
245 errors.push(anyhow!(
246 "{} = {:?} must be a finite number larger than zero!",
247 field_name,
248 field_val
249 ));
250 }
251}
252
253pub fn si_chk_num_eqz<T>(errors: &mut ValidationErrors, field_val: &T, field_name: &str)
255where
256 T: Debug + PartialEq + ConstZero,
257{
258 if *field_val != T::ZERO {
259 errors.push(anyhow!("{} = {:?} must equal zero!", field_name, field_val));
260 }
261}
262
263macro_rules! early_err {
264 ($errors:expr, $name:expr) => {
265 if !$errors.is_empty() {
266 $errors.push(anyhow!("{} validation unfinished!", $name));
267 return Err($errors);
268 }
269 };
270}
271
272macro_rules! early_fake_ok {
273 ($self:expr) => {
274 if $self.is_fake() {
275 return Ok(());
276 }
277 };
278}
279
280pub(crate) use {early_err, early_fake_ok};