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