use serde::{Deserialize, Serialize};
use std::fmt;
use time::OffsetDateTime;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
pub struct MetricKey(String);
impl MetricKey {
pub fn new(key: impl Into<String>) -> Self {
Self(key.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for MetricKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(rename_all = "snake_case")]
pub enum Unit {
Count,
Percent,
Bytes,
Milliseconds,
PerMinute,
Custom(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
pub struct Metric {
pub key: MetricKey,
pub value: f64,
pub unit: Unit,
#[serde(with = "time::serde::rfc3339")]
#[cfg_attr(feature = "ts", ts(type = "string"))]
pub at: OffsetDateTime,
}
impl Metric {
pub fn new(key: MetricKey, value: f64, unit: Unit, at: OffsetDateTime) -> Self {
Self {
key,
value,
unit,
at,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
pub struct Trend {
pub current: f64,
pub previous: f64,
}
impl Trend {
pub fn new(current: f64, previous: f64) -> Self {
Self { current, previous }
}
pub fn change_ratio(&self) -> Option<f64> {
if self.previous == 0.0 {
None
} else {
Some((self.current - self.previous) / self.previous)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn change_ratio_reports_a_drop() {
let trend = Trend::new(60.0, 100.0);
assert_eq!(trend.change_ratio(), Some(-0.4));
}
#[test]
fn change_ratio_from_zero_is_undefined() {
assert_eq!(Trend::new(10.0, 0.0).change_ratio(), None);
}
}