1use std::collections::{BTreeMap, BTreeSet};
12
13use uuid::Uuid;
14
15#[cfg(feature = "serde")]
16use crate::serde_utils::*;
17
18#[derive(Clone, Debug, PartialEq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct Metadata {
22 #[cfg_attr(feature = "serde", serde(rename = "id"))]
24 _id: Uuid,
25 pub name: String,
27 pub kind: String,
29 #[cfg_attr(
31 feature = "serde",
32 serde(
33 default,
34 serialize_with = "sort_alphabetically",
35 skip_serializing_if = "is_empty_set"
36 )
37 )]
38 pub labels: BTreeSet<String>,
39 #[cfg_attr(
41 feature = "serde",
42 serde(
43 default,
44 serialize_with = "sort_alphabetically",
45 skip_serializing_if = "is_empty_map"
46 )
47 )]
48 pub weights: BTreeMap<String, f64>,
49 #[cfg_attr(
51 feature = "serde",
52 serde(
53 default,
54 serialize_with = "sort_alphabetically",
55 skip_serializing_if = "is_empty_map"
56 )
57 )]
58 pub annotations: BTreeMap<String, serde_json::Value>,
59}
60impl Default for Metadata {
61 fn default() -> Self {
62 let _id = Uuid::new_v4();
63 Self {
64 _id,
65 name: _id.to_string(),
66 kind: "default".to_string(),
67 labels: BTreeSet::<String>::default(),
68 weights: BTreeMap::<String, f64>::default(),
69 annotations: BTreeMap::<String, serde_json::Value>::default(),
70 }
71 }
72}
73impl Metadata {
74 pub fn new(
76 id: Option<Uuid>,
77 name: Option<String>,
78 kind: Option<String>,
79 labels: Option<BTreeSet<String>>,
80 weights: Option<BTreeMap<String, f64>>,
81 annotations: Option<BTreeMap<String, serde_json::Value>>,
82 ) -> Self {
83 let _id = match id {
84 None => Uuid::new_v4(),
85 Some(x) => x,
86 };
87 Self {
88 _id,
89 name: name.unwrap_or_else(|| _id.to_string()),
90 kind: kind.unwrap_or_else(|| "default".to_string()),
91 labels: labels.unwrap_or_default(),
92 weights: weights.unwrap_or_default(),
93 annotations: annotations.unwrap_or_default(),
94 }
95 }
96 pub fn id(&self) -> &Uuid {
98 &self._id
99 }
100 pub fn set_id(&mut self, value: Option<Uuid>) {
103 self._id = value.unwrap_or_else(Uuid::new_v4);
104 }
105}
106
107pub trait Meta {
108 fn get_meta(&self) -> &Metadata;
109 fn id(&self) -> &Uuid {
110 &self.get_meta()._id
111 }
112 fn name(&self) -> &str {
113 self.get_meta().name.as_str()
114 }
115 fn kind(&self) -> &str {
116 self.get_meta().kind.as_str()
117 }
118 fn labels(&self) -> &BTreeSet<String> {
119 &self.get_meta().labels
120 }
121 fn weights(&self) -> &BTreeMap<String, f64> {
122 &self.get_meta().weights
123 }
124 fn annotations(&self) -> &BTreeMap<String, serde_json::Value> {
125 &self.get_meta().annotations
126 }
127}
128
129#[derive(Clone, Debug, Default, PartialEq)]
130#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
131pub enum Aggregator {
132 #[default]
133 Binary,
134 Kinds,
135 Labels,
136 Weights,
137 Annotations,
138}
139
140#[derive(Clone, Debug, Default, PartialEq)]
141#[cfg_attr(
142 feature = "serde",
143 derive(serde::Serialize, serde::Deserialize),
144 serde(default)
145)]
146pub struct Aggregate {
147 pub items: isize,
149 pub kinds: BTreeMap<String, isize>,
151 pub labels: BTreeMap<String, isize>,
153 pub weights: BTreeMap<String, f64>,
155 pub annotations: BTreeMap<String, isize>,
157}
158impl Aggregate {
159 pub fn new(data: &[&Metadata]) -> Self {
160 let mut agg = Self::default();
161 for &d in data.iter() {
162 agg.add(d)
163 }
164 agg
165 }
166 pub fn add(&mut self, item: &Metadata) {
168 self.items += 1;
169 *self.kinds.entry(item.kind.clone()).or_insert(0) += 1;
170 for label in item.labels.iter() {
171 *self.labels.entry(label.clone()).or_insert(0) += 1;
172 }
173 for (key, value) in item.weights.iter() {
174 *self.weights.entry(key.clone()).or_insert(0.0) += value;
175 }
176 for key in item.annotations.keys() {
177 *self.annotations.entry(key.clone()).or_insert(0) += 1;
178 }
179 }
180 pub fn subtract(&mut self, item: &Metadata) {
182 self.items -= 1;
183 if let Some(&num) = self.kinds.get(&item.kind) {
184 if num <= 1 {
185 self.kinds.remove(&item.kind);
186 } else {
187 *self.kinds.entry(item.kind.clone()).or_insert(1) -= 1;
188 }
189 }
190 for label in item.labels.iter() {
191 if let Some(&num) = self.labels.get(label) {
192 if num <= 1 {
193 self.labels.remove(label);
194 } else {
195 *self.labels.entry(label.clone()).or_insert(1) -= 1;
196 }
197 }
198 }
199 for (key, value) in item.weights.iter() {
200 *self.weights.entry(key.clone()).or_insert(0.0) -= value;
201 }
202 for key in item.annotations.keys() {
203 if let Some(&num) = self.annotations.get(key) {
204 if num <= 1 {
205 self.annotations.remove(key);
206 } else {
207 *self.annotations.entry(key.clone()).or_insert(1) -= 1;
208 }
209 }
210 }
211 }
212 pub fn extend(&mut self, other: Self) -> &Self {
214 self.items += other.items;
215 for (key, value) in other.kinds {
216 *self.kinds.entry(key).or_insert(0) += value;
217 }
218 for (key, value) in other.labels {
219 *self.labels.entry(key).or_insert(0) += value;
220 }
221 for (key, value) in other.weights {
222 *self.weights.entry(key).or_insert(0.0) += value;
223 }
224 for (key, value) in other.annotations {
225 *self.annotations.entry(key).or_insert(0) += value;
226 }
227 self
228 }
229 pub fn sum(
231 &self,
232 aggregator: &Aggregator,
233 fields: Option<&BTreeSet<String>>,
234 absolute: bool,
235 ) -> f64 {
236 match aggregator {
237 Aggregator::Binary => {
238 if self.items > 0 {
239 1.0
240 } else {
241 0.0
242 }
243 }
244 Aggregator::Kinds => match fields {
245 None => self.kinds.values().sum::<isize>() as f64,
246 Some(fields) => fields
247 .iter()
248 .map(|field| self.value(aggregator, field, absolute))
249 .sum(),
250 },
251 Aggregator::Labels => match fields {
252 None => self.labels.values().sum::<isize>() as f64,
253 Some(fields) => fields
254 .iter()
255 .map(|field| self.value(aggregator, field, absolute))
256 .sum(),
257 },
258 Aggregator::Weights => match fields {
259 None => self.weights.values().sum(),
260 Some(fields) => fields
261 .iter()
262 .map(|field| self.value(aggregator, field, absolute))
263 .sum(),
264 },
265 Aggregator::Annotations => match fields {
266 None => self.annotations.values().sum::<isize>() as f64,
267 Some(fields) => fields
268 .iter()
269 .map(|field| self.value(aggregator, field, absolute))
270 .sum(),
271 },
272 }
273 }
274 pub fn value<S: AsRef<str>>(&self, aggregator: &Aggregator, field: &S, absolute: bool) -> f64 {
276 let field = field.as_ref();
277 let value = match aggregator {
278 Aggregator::Binary => 1.0,
279 Aggregator::Kinds => self.kinds.get(field).map(|&x| x as f64).unwrap_or(0.0),
280 Aggregator::Labels => self.labels.get(field).map(|&x| x as f64).unwrap_or(0.0),
281 Aggregator::Weights => self.weights.get(field).copied().unwrap_or(0.0),
282 Aggregator::Annotations => self
283 .annotations
284 .get(field)
285 .map(|&x| x as f64)
286 .unwrap_or(0.0),
287 };
288 if absolute { value.abs() } else { value }
289 }
290 pub fn fraction<S: AsRef<str>>(
292 &self,
293 aggregator: &Aggregator,
294 field: &S,
295 fields: Option<&BTreeSet<String>>,
296 absolute: bool,
297 ) -> f64 {
298 let sum = self.sum(aggregator, fields, absolute);
299 let value = self.value(aggregator, field, absolute);
300 if sum == 0.0 { 0.0 } else { value / sum }
301 }
302 pub fn fractions<S: AsRef<str>>(
304 &self,
305 aggregator: &Aggregator,
306 fields: &[S],
307 factor: f64,
308 absolute: bool,
309 ) -> Vec<f64> {
310 let all: BTreeSet<String> = fields.iter().map(|s| s.as_ref().to_owned()).collect();
311 let sum = self.sum(aggregator, Some(&all), absolute);
312 let factor = { if sum == 0.0 { 1.0 } else { factor / sum } };
313 fields
314 .iter()
315 .map(|field| factor * self.value(aggregator, field, absolute))
316 .collect()
317 }
318}
319
320#[derive(Clone, Debug, Default, PartialEq)]
322#[cfg_attr(
323 feature = "serde",
324 derive(serde::Serialize, serde::Deserialize),
325 serde(default)
326)]
327pub struct Domains {
328 pub bounds: BTreeMap<String, (f64, f64)>,
329}
330impl Domains {
331 pub fn update(&mut self, values: &BTreeMap<String, f64>) {
333 for (key, &value) in values.iter() {
334 if let Some(entry) = self.bounds.get_mut(key) {
335 entry.0 = entry.0.min(value);
336 entry.1 = entry.1.max(value);
337 } else {
338 self.bounds.insert(key.to_owned(), (value, value));
339 }
340 }
341 }
342 pub fn get<S: AsRef<str>>(&self, key: &S) -> Option<&(f64, f64)> {
344 self.bounds.get(key.as_ref())
345 }
346 pub fn interpolate<S: AsRef<str>>(&self, key: &S, value: f64) -> Option<f64> {
349 self.get(key).map(|&(lower, upper)| {
350 if lower == upper {
351 1.0
352 } else {
353 (value - lower) / (upper - lower)
354 }
355 })
356 }
357}
358
359#[derive(Clone, Default, Debug, PartialEq)]
361#[cfg_attr(
362 feature = "serde",
363 derive(serde::Serialize, serde::Deserialize),
364 serde(default)
365)]
366pub struct MetadataFilter {
367 pub kinds: Option<Vec<String>>,
368 pub labels: Option<BTreeSet<String>>,
369}
370impl MetadataFilter {
371 pub fn satisfies<T: Meta>(&self, instance: &T) -> bool {
373 self.satisfies_kinds(instance) && self.satisfies_labels(instance)
374 }
375
376 pub fn satisfies_kinds<T: Meta>(&self, instance: &T) -> bool {
378 if let Some(kinds) = &self.kinds {
379 kinds.contains(&instance.kind().to_owned())
380 } else {
381 true
382 }
383 }
384 pub fn satisfies_labels<T: Meta>(&self, instance: &T) -> bool {
386 {
387 if let Some(labels) = &self.labels {
388 !labels.is_disjoint(instance.labels())
389 } else {
390 true
391 }
392 }
393 }
394}