use_timeseries_store/
lib.rs1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7macro_rules! string_newtype {
8 ($(#[$meta:meta])* $name:ident) => {
9 $(#[$meta])*
10 #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
11 pub struct $name(String);
12
13 impl $name {
14 pub fn new(value: impl Into<String>) -> Self {
16 Self(value.into())
17 }
18
19 pub fn as_str(&self) -> &str {
21 &self.0
22 }
23 }
24
25 impl AsRef<str> for $name {
26 fn as_ref(&self) -> &str {
27 self.as_str()
28 }
29 }
30
31 impl From<String> for $name {
32 fn from(value: String) -> Self {
33 Self::new(value)
34 }
35 }
36
37 impl From<&str> for $name {
38 fn from(value: &str) -> Self {
39 Self::new(value)
40 }
41 }
42
43 impl fmt::Display for $name {
44 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
45 formatter.write_str(self.as_str())
46 }
47 }
48 };
49}
50
51string_newtype! {
52 SeriesId
54}
55string_newtype! {
56 MetricName
58}
59
60#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
62pub struct Timestamp(SystemTime);
63
64impl Timestamp {
65 pub const fn new(value: SystemTime) -> Self {
67 Self(value)
68 }
69
70 pub fn now() -> Self {
72 Self(SystemTime::now())
73 }
74
75 pub const fn system_time(self) -> SystemTime {
77 self.0
78 }
79}
80
81impl fmt::Display for Timestamp {
82 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
83 match self.0.duration_since(UNIX_EPOCH) {
84 Ok(duration) => write!(formatter, "{}", duration.as_secs()),
85 Err(error) => write!(formatter, "-{}", error.duration().as_secs()),
86 }
87 }
88}
89
90#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
92pub struct TimeSeriesValue(f64);
93
94impl TimeSeriesValue {
95 pub const fn new(value: f64) -> Self {
97 Self(value)
98 }
99
100 pub const fn value(self) -> f64 {
102 self.0
103 }
104}
105
106impl fmt::Display for TimeSeriesValue {
107 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(formatter, "{}", self.0)
109 }
110}
111
112#[derive(Clone, Debug, PartialEq)]
114pub struct TimeSeriesPoint {
115 series_id: SeriesId,
116 metric_name: MetricName,
117 timestamp: Timestamp,
118 value: TimeSeriesValue,
119}
120
121impl TimeSeriesPoint {
122 pub fn new(
124 series_id: SeriesId,
125 metric_name: MetricName,
126 timestamp: Timestamp,
127 value: TimeSeriesValue,
128 ) -> Self {
129 Self {
130 series_id,
131 metric_name,
132 timestamp,
133 value,
134 }
135 }
136
137 pub const fn series_id(&self) -> &SeriesId {
139 &self.series_id
140 }
141
142 pub const fn metric_name(&self) -> &MetricName {
144 &self.metric_name
145 }
146
147 pub const fn timestamp(&self) -> Timestamp {
149 self.timestamp
150 }
151
152 pub const fn value(&self) -> TimeSeriesValue {
154 self.value
155 }
156}
157
158#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
160pub struct RetentionWindow(Duration);
161
162impl RetentionWindow {
163 pub const fn new(duration: Duration) -> Self {
165 Self(duration)
166 }
167
168 pub const fn duration(self) -> Duration {
170 self.0
171 }
172}
173
174#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
176pub struct SamplingInterval(Duration);
177
178impl SamplingInterval {
179 pub const fn new(duration: Duration) -> Self {
181 Self(duration)
182 }
183
184 pub const fn duration(self) -> Duration {
186 self.0
187 }
188}
189
190#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
192pub struct AggregationWindow(Duration);
193
194impl AggregationWindow {
195 pub const fn new(duration: Duration) -> Self {
197 Self(duration)
198 }
199
200 pub const fn duration(self) -> Duration {
202 self.0
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::{
209 AggregationWindow, MetricName, RetentionWindow, SamplingInterval, SeriesId,
210 TimeSeriesPoint, TimeSeriesValue, Timestamp,
211 };
212 use std::time::{Duration, UNIX_EPOCH};
213
214 #[test]
215 fn constructs_time_series_labels() {
216 assert_eq!(SeriesId::new("cpu:host-1").to_string(), "cpu:host-1");
217 assert_eq!(MetricName::new("cpu.usage").as_ref(), "cpu.usage");
218 }
219
220 #[test]
221 fn builds_points_and_windows() {
222 let point = TimeSeriesPoint::new(
223 SeriesId::new("host_1"),
224 MetricName::new("cpu.usage"),
225 Timestamp::new(UNIX_EPOCH + Duration::from_secs(10)),
226 TimeSeriesValue::new(0.75),
227 );
228 let retention = RetentionWindow::new(Duration::from_hours(24));
229 let sampling = SamplingInterval::new(Duration::from_mins(1));
230 let aggregation = AggregationWindow::new(Duration::from_mins(5));
231
232 assert_eq!(point.timestamp().to_string(), "10");
233 assert_eq!(point.value().to_string(), "0.75");
234 assert_eq!(retention.duration().as_secs(), 86_400);
235 assert_eq!(sampling.duration().as_secs(), 60);
236 assert_eq!(aggregation.duration().as_secs(), 300);
237 }
238}