use crate::numeric::Numeric;
use std::sync::OnceLock;
#[derive(Debug, Clone)]
pub struct NumericRange {
pub min: Numeric,
pub max: Numeric,
pub is_integer: bool,
pub is_js_number: bool,
}
static NUMERIC_RANGES: OnceLock<Vec<(&'static str, NumericRange)>> = OnceLock::new();
pub fn get_numeric_ranges() -> &'static [(&'static str, NumericRange)] {
NUMERIC_RANGES
.get_or_init(|| {
vec![
(
"int64",
NumericRange {
min: Numeric::new("-9223372036854775808").expect("valid numeric constant"),
max: Numeric::new("9223372036854775807").expect("valid numeric constant"),
is_integer: true,
is_js_number: false,
},
),
(
"int32",
NumericRange {
min: Numeric::new("-2147483648").expect("valid numeric constant"),
max: Numeric::new("2147483647").expect("valid numeric constant"),
is_integer: true,
is_js_number: true,
},
),
(
"int16",
NumericRange {
min: Numeric::new("-32768").expect("valid numeric constant"),
max: Numeric::new("32767").expect("valid numeric constant"),
is_integer: true,
is_js_number: true,
},
),
(
"int8",
NumericRange {
min: Numeric::new("-128").expect("valid numeric constant"),
max: Numeric::new("127").expect("valid numeric constant"),
is_integer: true,
is_js_number: true,
},
),
(
"uint64",
NumericRange {
min: Numeric::new("0").expect("valid numeric constant"),
max: Numeric::new("18446744073709551615").expect("valid numeric constant"),
is_integer: true,
is_js_number: false,
},
),
(
"uint32",
NumericRange {
min: Numeric::new("0").expect("valid numeric constant"),
max: Numeric::new("4294967295").expect("valid numeric constant"),
is_integer: true,
is_js_number: true,
},
),
(
"uint16",
NumericRange {
min: Numeric::new("0").expect("valid numeric constant"),
max: Numeric::new("65535").expect("valid numeric constant"),
is_integer: true,
is_js_number: true,
},
),
(
"uint8",
NumericRange {
min: Numeric::new("0").expect("valid numeric constant"),
max: Numeric::new("255").expect("valid numeric constant"),
is_integer: true,
is_js_number: true,
},
),
(
"safeint",
NumericRange {
min: Numeric::new("-9007199254740991").expect("valid numeric constant"),
max: Numeric::new("9007199254740991").expect("valid numeric constant"),
is_integer: true,
is_js_number: true,
},
),
(
"float32",
NumericRange {
min: Numeric::new("-3.4e38").expect("valid numeric constant"),
max: Numeric::new("3.4e38").expect("valid numeric constant"),
is_integer: false,
is_js_number: true,
},
),
(
"float64",
NumericRange {
min: Numeric::new("-1.7976931348623157e308")
.expect("valid numeric constant"),
max: Numeric::new("1.7976931348623157e308")
.expect("valid numeric constant"),
is_integer: false,
is_js_number: true,
},
),
]
})
.as_slice()
}
pub fn is_value_in_range(value: &Numeric, scalar_name: &str) -> bool {
for (name, range) in get_numeric_ranges() {
if *name == scalar_name {
return value >= &range.min && value <= &range.max;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_int32_range() {
let range = get_numeric_ranges()
.iter()
.find(|(n, _)| *n == "int32")
.map(|(_, r)| r)
.expect("valid numeric constant");
assert!(range.is_integer);
let val = Numeric::new("100").expect("valid numeric constant");
assert!(val >= range.min && val <= range.max);
let overflow = Numeric::new("3000000000").expect("valid numeric constant");
assert!(overflow > range.max);
}
#[test]
fn test_uint8_range() {
let range = get_numeric_ranges()
.iter()
.find(|(n, _)| *n == "uint8")
.map(|(_, r)| r)
.expect("valid numeric constant");
assert!(range.is_integer);
assert!(Numeric::new("0").expect("valid numeric constant") >= range.min);
assert!(Numeric::new("255").expect("valid numeric constant") <= range.max);
assert!(Numeric::new("256").expect("valid numeric constant") > range.max);
}
#[test]
fn test_is_value_in_range() {
assert!(is_value_in_range(
&Numeric::new("100").expect("valid numeric constant"),
"int32"
));
assert!(!is_value_in_range(
&Numeric::new("3000000000").expect("valid numeric constant"),
"int32"
));
assert!(is_value_in_range(
&Numeric::new("200").expect("valid numeric constant"),
"uint8"
));
assert!(!is_value_in_range(
&Numeric::new("300").expect("valid numeric constant"),
"uint8"
));
}
#[test]
fn test_all_ranges_present() {
let ranges = get_numeric_ranges();
let names: Vec<&str> = ranges.iter().map(|(n, _)| *n).collect();
assert!(names.contains(&"int8"));
assert!(names.contains(&"int16"));
assert!(names.contains(&"int32"));
assert!(names.contains(&"int64"));
assert!(names.contains(&"uint8"));
assert!(names.contains(&"uint16"));
assert!(names.contains(&"uint32"));
assert!(names.contains(&"uint64"));
assert!(names.contains(&"safeint"));
assert!(names.contains(&"float32"));
assert!(names.contains(&"float64"));
}
}