zino_http/timing/
timing_metric.rs

1use std::{fmt, time::Duration};
2use zino_core::SharedString;
3
4/// A metric of the performance timing.
5#[derive(Debug, Clone)]
6pub struct TimingMetric {
7    /// Metric name.
8    name: SharedString,
9    /// Optional description.
10    description: Option<SharedString>,
11    /// Timing duration. A zero value means that it does not exist.
12    duration: Duration,
13}
14
15impl TimingMetric {
16    /// Creates a new instance.
17    #[inline]
18    pub fn new(
19        name: SharedString,
20        description: Option<SharedString>,
21        duration: Option<Duration>,
22    ) -> Self {
23        Self {
24            name,
25            description,
26            duration: duration.unwrap_or_default(),
27        }
28    }
29
30    /// Returns the name.
31    #[inline]
32    pub fn name(&self) -> &str {
33        self.name.as_ref()
34    }
35
36    /// Returns the description.
37    #[inline]
38    pub fn description(&self) -> Option<&str> {
39        self.description.as_deref()
40    }
41
42    /// Returns the timing duration.
43    #[inline]
44    pub fn duration(&self) -> Option<Duration> {
45        let duration = self.duration;
46        (duration > Duration::ZERO).then_some(duration)
47    }
48}
49
50impl fmt::Display for TimingMetric {
51    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52        let name = self.name();
53        if let Some(duration) = self.duration() {
54            let mut buffer = ryu::Buffer::new();
55            let millis = (duration.as_micros() as f64) / 1000.0;
56            let duration_millis = buffer.format_finite(millis);
57            if let Some(description) = self.description() {
58                write!(f, "{name};desc={description};dur={duration_millis}")
59            } else {
60                write!(f, "{name};dur={duration_millis}")
61            }
62        } else if let Some(description) = self.description() {
63            write!(f, "{name};desc={description}")
64        } else {
65            write!(f, "{name}")
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::TimingMetric;
73    use std::time::Duration;
74
75    #[test]
76    fn it_formats_timing_metric() {
77        let cache_miss_metric = TimingMetric::new("miss".into(), None, None);
78        assert_eq!(format!("{cache_miss_metric}"), "miss");
79
80        let db_query_metric = TimingMetric::new(
81            "db".into(),
82            Some("query".into()),
83            Some(Duration::from_secs_f64(0.0024635)),
84        );
85        assert_eq!(format!("{db_query_metric}"), "db;desc=query;dur=2.463");
86
87        let total_timing_metric =
88            TimingMetric::new("total".into(), None, Some(Duration::from_secs_f64(0.01082)));
89        assert_eq!(format!("{total_timing_metric}"), "total;dur=10.82");
90    }
91}