cortexkit_model_batch/
number.rs1use serde_json::{value::RawValue, Number};
15
16pub type NumberText = Box<RawValue>;
18
19pub fn is_number_literal(text: &str) -> bool {
23 let bytes = text.as_bytes();
24 let mut at = 0;
25 let digits = |at: &mut usize| {
26 let start = *at;
27 while *at < bytes.len() && bytes[*at].is_ascii_digit() {
28 *at += 1;
29 }
30 *at - start
31 };
32 if bytes.get(at) == Some(&b'-') {
33 at += 1;
34 }
35 match bytes.get(at) {
36 Some(b'0') => at += 1,
37 Some(b'1'..=b'9') => {
38 digits(&mut at);
39 }
40 _ => return false,
41 }
42 if bytes.get(at) == Some(&b'.') {
43 at += 1;
44 if digits(&mut at) == 0 {
45 return false;
46 }
47 }
48 if matches!(bytes.get(at), Some(b'e' | b'E')) {
49 at += 1;
50 if matches!(bytes.get(at), Some(b'+' | b'-')) {
51 at += 1;
52 }
53 if digits(&mut at) == 0 {
54 return false;
55 }
56 }
57 at == bytes.len()
58}
59
60pub fn is_number_text(raw: &RawValue) -> bool {
62 is_number_literal(raw.get())
63}
64
65pub fn number_text(text: &str) -> Result<NumberText, String> {
67 if !is_number_literal(text) {
68 return Err(format!("{text:?} is not a JSON number"));
69 }
70 RawValue::from_string(text.to_owned()).map_err(|e| e.to_string())
71}
72
73pub fn number_text_of(number: &Number) -> NumberText {
76 RawValue::from_string(number.to_string()).expect("a Number's text is JSON")
77}
78
79pub fn same_text(a: Option<&RawValue>, b: Option<&RawValue>) -> bool {
81 a.map(RawValue::get) == b.map(RawValue::get)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn number_literals() {
90 for good in ["0", "-0", "0.10", "1e-7", "1E+30", "12.500", "-3"] {
91 assert!(is_number_literal(good), "{good}");
92 }
93 for bad in [
94 "", "-", "01", "1.", ".5", "1e", "+1", " 1", "1 ", "NaN", "\"1\"", "null",
95 ] {
96 assert!(!is_number_literal(bad), "{bad}");
97 }
98 }
99
100 #[test]
101 fn number_text_keeps_the_spelling_through_a_decode() {
102 let decoded: Vec<NumberText> = serde_json::from_str("[0.10,1e-7,1E+30,12.500]").unwrap();
103 let texts: Vec<&str> = decoded.iter().map(|raw| raw.get()).collect();
104 assert_eq!(texts, ["0.10", "1e-7", "1E+30", "12.500"]);
105 assert_eq!(
106 serde_json::to_string(&decoded).unwrap(),
107 "[0.10,1e-7,1E+30,12.500]"
108 );
109 assert!(number_text("0.10").is_ok());
110 assert!(number_text("\"x\"").is_err());
111 }
112}