json_eval_rs/utils/
mod.rs1use serde_json::Value;
7use std::cell::RefCell;
8
9#[cfg(feature = "wasm")]
10use wasm_bindgen::prelude::*;
11
12#[cfg(feature = "wasm")]
13#[wasm_bindgen]
14extern "C" {
15 #[wasm_bindgen(js_namespace = console)]
16 pub fn log(s: &str);
17}
18
19#[macro_export]
22macro_rules! debug_log {
23 ($($t:tt)*) => {
24 let msg = format!($($t)*);
25 #[cfg(feature = "wasm")]
26 {
27 $crate::utils::log(&format!("[WASM DEBUG] {}", msg));
28 }
29 #[cfg(not(feature = "wasm"))]
30 {
31 println!("[WASM DEBUG] {}", msg);
32 }
33 }
34}
35
36thread_local! {
38 static TIMING_ENABLED: RefCell<bool> = RefCell::new(std::env::var("JSONEVAL_TIMING").is_ok());
39 static DEBUG_CACHE_ENABLED: RefCell<bool> = RefCell::new(std::env::var("JSONEVAL_DEBUG_CACHE").is_ok());
40 static TIMING_DATA: RefCell<Vec<(String, std::time::Duration)>> = RefCell::new(Vec::new());
41}
42
43#[inline]
45pub fn is_timing_enabled() -> bool {
46 TIMING_ENABLED.with(|enabled| *enabled.borrow())
47}
48
49#[inline]
51pub fn is_debug_cache_enabled() -> bool {
52 DEBUG_CACHE_ENABLED.with(|enabled| *enabled.borrow())
53}
54
55pub fn enable_timing() {
57 TIMING_ENABLED.with(|enabled| {
58 *enabled.borrow_mut() = true;
59 });
60}
61
62pub fn enable_debug_cache() {
63 DEBUG_CACHE_ENABLED.with(|enabled| {
64 *enabled.borrow_mut() = true;
65 });
66}
67
68pub fn disable_timing() {
70 TIMING_ENABLED.with(|enabled| {
71 *enabled.borrow_mut() = false;
72 });
73}
74
75#[inline]
77pub fn record_timing(label: &str, duration: std::time::Duration) {
78 if is_timing_enabled() {
79 TIMING_DATA.with(|data| {
80 data.borrow_mut().push((label.to_string(), duration));
81 });
82 }
83}
84
85pub fn print_timing_summary() {
87 if !is_timing_enabled() {
88 return;
89 }
90
91 TIMING_DATA.with(|data| {
92 let timings = data.borrow();
93 if timings.is_empty() {
94 return;
95 }
96
97 eprintln!("\nš Timing Summary (JSONEVAL_TIMING enabled)");
98 eprintln!("{}", "=".repeat(60));
99
100 let mut total = std::time::Duration::ZERO;
101 for (label, duration) in timings.iter() {
102 eprintln!("{:40} {:>12?}", label, duration);
103 total += *duration;
104 }
105
106 eprintln!("{}", "=".repeat(60));
107 eprintln!("{:40} {:>12?}", "TOTAL", total);
108 eprintln!();
109 });
110}
111
112pub fn clear_timing_data() {
114 TIMING_DATA.with(|data| {
115 data.borrow_mut().clear();
116 });
117}
118
119#[macro_export]
121macro_rules! time_block {
122 ($label:expr, $block:block) => {{
123 let _start = if $crate::utils::is_timing_enabled() {
124 Some(std::time::Instant::now())
125 } else {
126 None
127 };
128 let result = $block;
129 if let Some(start) = _start {
130 $crate::utils::record_timing($label, start.elapsed());
131 }
132 result
133 }};
134}
135
136pub fn clean_float_noise(value: Value) -> Value {
139 const EPSILON: f64 = 1e-10;
140
141 match value {
142 Value::Number(n) => {
143 if let Some(f) = n.as_f64() {
144 if f.abs() < EPSILON {
145 Value::Number(serde_json::Number::from(0))
146 } else if f.fract().abs() < EPSILON {
147 Value::Number(serde_json::Number::from(f.round() as i64))
148 } else {
149 Value::Number(n)
150 }
151 } else {
152 Value::Number(n)
153 }
154 }
155 Value::Array(arr) => Value::Array(arr.into_iter().map(clean_float_noise).collect()),
156 Value::Object(obj) => Value::Object(
157 obj.into_iter()
158 .map(|(k, v)| (k, clean_float_noise(v)))
159 .collect(),
160 ),
161 _ => value,
162 }
163}
164
165#[inline(always)]
166pub fn clean_float_noise_scalar(value: Value) -> Value {
167 const EPSILON: f64 = 1e-10;
168
169 match value {
170 Value::Number(ref n) => {
171 if let Some(f) = n.as_f64() {
172 if f.abs() < EPSILON {
173 Value::Number(serde_json::Number::from(0))
174 } else if f.fract().abs() < EPSILON {
175 Value::Number(serde_json::Number::from(f.round() as i64))
176 } else {
177 value
178 }
179 } else {
180 value
181 }
182 }
183 Value::Array(_) | Value::Object(_) => clean_float_noise(value),
184 _ => value,
185 }
186}