saluki_core/data_model/event/metric/value/
set.rs

1use std::{collections::HashSet, fmt, num::NonZeroU64};
2
3use super::{
4    iter::{PointsIter, PointsIterRef},
5    TimestampedValue, TimestampedValues,
6};
7
8/// A set of set points.
9///
10/// Used to represent the data points of sets. Each data point is attached to an optional timestamp.
11///
12/// Sets are an exception to the common scalar or sketch-based points, where actual string values are held instead.
13/// These are generally meant to represent some unique set of values, whose count is then used as the actual output
14/// metric.
15#[derive(Clone, Debug, Eq, PartialEq)]
16pub struct SetPoints(TimestampedValues<HashSet<String>, 1>);
17
18impl SetPoints {
19    pub(super) fn inner(&self) -> &TimestampedValues<HashSet<String>, 1> {
20        &self.0
21    }
22
23    pub(super) fn inner_mut(&mut self) -> &mut TimestampedValues<HashSet<String>, 1> {
24        &mut self.0
25    }
26
27    pub(super) fn drain_timestamped(&mut self) -> Self {
28        Self(self.0.drain_timestamped())
29    }
30
31    pub(super) fn split_at_timestamp(&mut self, timestamp: u64) -> Option<Self> {
32        self.0.split_at_timestamp(timestamp).map(Self)
33    }
34
35    /// Returns `true` if this set is empty.
36    pub fn is_empty(&self) -> bool {
37        self.0.values.is_empty()
38    }
39
40    /// Returns the number of points in this set.
41    pub fn len(&self) -> usize {
42        self.0.values.len()
43    }
44
45    /// Merges another set of points into this one.
46    ///
47    /// If a point with the same timestamp exists in both sets, the sets will be merged together. Otherwise, the points
48    /// will appended to the end of the set.
49    pub fn merge(&mut self, other: Self) {
50        let mut needs_sort = false;
51        for other_value in other.0.values {
52            if let Some(existing_value) = self
53                .0
54                .values
55                .iter_mut()
56                .find(|value| value.timestamp == other_value.timestamp)
57            {
58                existing_value.value.extend(other_value.value);
59            } else {
60                self.0.values.push(other_value);
61                needs_sort = true;
62            }
63        }
64
65        if needs_sort {
66            self.0.sort_by_timestamp();
67        }
68    }
69}
70
71impl From<String> for SetPoints {
72    fn from(value: String) -> Self {
73        Self(TimestampedValue::from(HashSet::from([value])).into())
74    }
75}
76
77impl<'a> From<&'a str> for SetPoints {
78    fn from(value: &'a str) -> Self {
79        Self(TimestampedValue::from(HashSet::from([value.to_string()])).into())
80    }
81}
82
83impl<'a> From<(u64, &'a str)> for SetPoints {
84    fn from((ts, value): (u64, &'a str)) -> Self {
85        Self(TimestampedValue::from((ts, HashSet::from([value.to_string()]))).into())
86    }
87}
88
89impl<'a, const N: usize> From<[&'a str; N]> for SetPoints {
90    fn from(values: [&'a str; N]) -> Self {
91        Self(TimestampedValue::from(HashSet::from_iter(values.into_iter().map(|s| s.to_string()))).into())
92    }
93}
94
95impl<'a, const N: usize> From<(u64, [&'a str; N])> for SetPoints {
96    fn from((ts, values): (u64, [&'a str; N])) -> Self {
97        Self(TimestampedValue::from((ts, values.into_iter().map(|s| s.to_string()).collect())).into())
98    }
99}
100
101impl<'a, const N: usize> From<[(u64, &'a str); N]> for SetPoints {
102    fn from(values: [(u64, &'a str); N]) -> Self {
103        Self(
104            values
105                .iter()
106                .map(|(ts, value)| TimestampedValue::from((*ts, HashSet::from([value.to_string()]))))
107                .into(),
108        )
109    }
110}
111
112impl<'a, const N: usize> From<[(u64, &'a [&'a str]); N]> for SetPoints {
113    fn from(values: [(u64, &'a [&'a str]); N]) -> Self {
114        Self(
115            values
116                .iter()
117                .map(|(ts, values)| TimestampedValue::from((*ts, values.iter().map(|s| s.to_string()).collect())))
118                .into(),
119        )
120    }
121}
122
123impl IntoIterator for SetPoints {
124    type Item = (Option<NonZeroU64>, f64);
125    type IntoIter = PointsIter;
126
127    fn into_iter(self) -> Self::IntoIter {
128        PointsIter::set(self.0.values.into_iter())
129    }
130}
131
132impl<'a> IntoIterator for &'a SetPoints {
133    type Item = (Option<NonZeroU64>, f64);
134    type IntoIter = PointsIterRef<'a>;
135
136    fn into_iter(self) -> Self::IntoIter {
137        PointsIterRef::set(self.0.values.iter())
138    }
139}
140
141impl fmt::Display for SetPoints {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(f, "[")?;
144        for (i, point) in self.0.values.iter().enumerate() {
145            if i > 0 {
146                write!(f, ",")?;
147            }
148
149            let ts = point.timestamp.map(|ts| ts.get()).unwrap_or_default();
150            write!(f, "({}, [", ts)?;
151            for (j, value) in point.value.iter().enumerate() {
152                if j > 0 {
153                    write!(f, ",")?;
154                }
155                write!(f, "{}", value)?;
156            }
157            write!(f, "])")?;
158        }
159        write!(f, "]")
160    }
161}