substrate_data/schema/reader/
numbers.rs1use super::Reader;
4use crate::diagnostic::Problem;
5use crate::model::number::whole_to_i64;
6use crate::model::value::Seg;
7
8pub trait Numbers {
10 fn numbers(&self, out: &mut Vec<f64>);
12}
13
14macro_rules! numbers {
15 ($($t:ty),*) => {$(
16 impl Numbers for $t {
17 fn numbers(&self, out: &mut Vec<f64>) {
18 out.push(f64::from(*self));
19 }
20 }
21 )*};
22}
23numbers!(i8, i16, i32, u8, u16, u32, f32, f64);
24
25macro_rules! wide_numbers {
26 ($($t:ty),*) => {$(
27 impl Numbers for $t {
28 #[expect(
29 clippy::cast_precision_loss,
30 reason = "a range is checked on the nearest float"
31 )]
32 fn numbers(&self, out: &mut Vec<f64>) {
33 out.push(*self as f64);
34 }
35 }
36 )*};
37}
38wide_numbers!(i64, u64, usize);
39
40impl<T: Numbers> Numbers for Option<T> {
41 fn numbers(&self, out: &mut Vec<f64>) {
42 if let Some(x) = self {
43 x.numbers(out);
44 }
45 }
46}
47
48impl<T: Numbers> Numbers for Vec<T> {
49 fn numbers(&self, out: &mut Vec<f64>) {
50 for x in self {
51 x.numbers(out);
52 }
53 }
54}
55
56impl<T: Numbers, const N: usize> Numbers for [T; N] {
57 fn numbers(&self, out: &mut Vec<f64>) {
58 for x in self {
59 x.numbers(out);
60 }
61 }
62}
63
64impl Reader<'_> {
65 pub fn in_range<T: Numbers>(
67 &mut self,
68 key: &str,
69 value: Option<T>,
70 lo: f64,
71 hi: f64,
72 ) -> Option<T> {
73 let v = value?;
74 let mut ns = Vec::new();
75 v.numbers(&mut ns);
76 match ns.into_iter().find(|n| !(lo..=hi).contains(n)) {
77 None => Some(v),
78 Some(n) => {
79 let show = |x: f64| {
80 whole_to_i64(x).map_or_else(|| x.to_string(), |whole| whole.to_string())
81 };
82 let bad = show(n);
83 self.at(Seg::Key(key.to_string()), |cx| {
84 cx.problem_of(
85 Problem::OutOfRange,
86 format!("{bad} is out of range ({}..={})", show(lo), show(hi)),
87 );
88 });
89 None
90 }
91 }
92 }
93}