json_eval_rs/utils/
mod.rs1use serde_json::Value;
2use std::cell::RefCell;
3
4
5thread_local! {
7 static TIMING_ENABLED: RefCell<bool> = RefCell::new(std::env::var("JSONEVAL_TIMING").is_ok());
8 static TIMING_DATA: RefCell<Vec<(String, std::time::Duration)>> = RefCell::new(Vec::new());
9}
10
11#[inline]
13pub fn is_timing_enabled() -> bool {
14 TIMING_ENABLED.with(|enabled| *enabled.borrow())
15}
16
17pub fn enable_timing() {
19 TIMING_ENABLED.with(|enabled| {
20 *enabled.borrow_mut() = true;
21 });
22}
23
24pub fn disable_timing() {
26 TIMING_ENABLED.with(|enabled| {
27 *enabled.borrow_mut() = false;
28 });
29}
30
31#[inline]
33pub fn record_timing(label: &str, duration: std::time::Duration) {
34 if is_timing_enabled() {
35 TIMING_DATA.with(|data| {
36 data.borrow_mut().push((label.to_string(), duration));
37 });
38 }
39}
40
41pub fn print_timing_summary() {
43 if !is_timing_enabled() {
44 return;
45 }
46
47 TIMING_DATA.with(|data| {
48 let timings = data.borrow();
49 if timings.is_empty() {
50 return;
51 }
52
53 eprintln!("\nš Timing Summary (JSONEVAL_TIMING enabled)");
54 eprintln!("{}", "=".repeat(60));
55
56 let mut total = std::time::Duration::ZERO;
57 for (label, duration) in timings.iter() {
58 eprintln!("{:40} {:>12?}", label, duration);
59 total += *duration;
60 }
61
62 eprintln!("{}", "=".repeat(60));
63 eprintln!("{:40} {:>12?}", "TOTAL", total);
64 eprintln!();
65 });
66}
67
68pub fn clear_timing_data() {
70 TIMING_DATA.with(|data| {
71 data.borrow_mut().clear();
72 });
73}
74
75#[macro_export]
77macro_rules! time_block {
78 ($label:expr, $block:block) => {{
79 let _start = if $crate::utils::is_timing_enabled() {
80 Some(std::time::Instant::now())
81 } else {
82 None
83 };
84 let result = $block;
85 if let Some(start) = _start {
86 $crate::utils::record_timing($label, start.elapsed());
87 }
88 result
89 }};
90}
91
92pub fn clean_float_noise(value: Value) -> Value {
95 const EPSILON: f64 = 1e-10;
96
97 match value {
98 Value::Number(n) => {
99 if let Some(f) = n.as_f64() {
100 if f.abs() < EPSILON {
101 Value::Number(serde_json::Number::from(0))
102 } else if f.fract().abs() < EPSILON {
103 Value::Number(serde_json::Number::from(f.round() as i64))
104 } else {
105 Value::Number(n)
106 }
107 } else {
108 Value::Number(n)
109 }
110 }
111 Value::Array(arr) => Value::Array(arr.into_iter().map(clean_float_noise).collect()),
112 Value::Object(obj) => Value::Object(
113 obj.into_iter()
114 .map(|(k, v)| (k, clean_float_noise(v)))
115 .collect(),
116 ),
117 _ => value,
118 }
119}
120
121#[inline(always)]
122pub fn clean_float_noise_scalar(value: Value) -> Value {
123 const EPSILON: f64 = 1e-10;
124
125 match value {
126 Value::Number(ref n) => {
127 if let Some(f) = n.as_f64() {
128 if f.abs() < EPSILON {
129 Value::Number(serde_json::Number::from(0))
130 } else if f.fract().abs() < EPSILON {
131 Value::Number(serde_json::Number::from(f.round() as i64))
132 } else {
133 value
134 }
135 } else {
136 value
137 }
138 }
139 Value::Array(_) | Value::Object(_) => clean_float_noise(value),
140 _ => value,
141 }
142}
143