Skip to main content

diagnostics/
metrics.rs

1use serde::{Deserialize, Serialize};
2use web_time::Instant;
3
4/// Timing for a single phase of validation (parsing, schema validation, rule evaluation, etc.).
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
7#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
8#[serde(rename_all = "camelCase")]
9pub struct PhaseMetric {
10    pub duration_ms: f64,
11}
12
13/// Creates a `PhaseMetric` from the elapsed time since `start`.
14pub fn phase_metric(start: Instant) -> PhaseMetric {
15    PhaseMetric { duration_ms: start.elapsed().as_secs_f64() * 1000.0 }
16}
17
18#[cfg(test)]
19mod tests {
20    use super::*;
21    use std::thread;
22    use std::time::Duration;
23
24    #[test]
25    fn phase_metric_captures_nonzero_elapsed_time() {
26        let start = Instant::now();
27        thread::sleep(Duration::from_millis(10));
28        let metric = phase_metric(start);
29        assert!(metric.duration_ms >= 5.0, "Expected at least 5ms, got {}ms", metric.duration_ms);
30    }
31
32    #[test]
33    fn phase_metric_serializes_camel_case_field_name() {
34        let metric = PhaseMetric { duration_ms: 42.5 };
35        let json = serde_json::to_string(&metric).unwrap();
36        assert!(json.contains("durationMs"));
37        assert!(json.contains("42.5"));
38    }
39
40    #[test]
41    fn phase_metric_serde_round_trips() {
42        let original = PhaseMetric { duration_ms: 123.456 };
43        let json = serde_json::to_string(&original).unwrap();
44        let deserialized: PhaseMetric = serde_json::from_str(&json).unwrap();
45        assert!((deserialized.duration_ms - 123.456).abs() < f64::EPSILON);
46    }
47}