#[cfg(feature = "serde")]
use crate::serde_utils::*;
use std::collections::{BTreeMap, BTreeSet};
use uuid::Uuid;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Metadata {
#[cfg_attr(feature = "serde", serde(rename = "id"))]
_id: Uuid,
pub name: String,
pub kind: String,
#[cfg_attr(
feature = "serde",
serde(
default,
serialize_with = "sort_alphabetically",
skip_serializing_if = "is_empty_set"
)
)]
pub labels: BTreeSet<String>,
#[cfg_attr(
feature = "serde",
serde(
default,
serialize_with = "sort_alphabetically",
skip_serializing_if = "is_empty_map"
)
)]
pub weights: BTreeMap<String, f64>,
#[cfg_attr(
feature = "serde",
serde(
default,
serialize_with = "sort_alphabetically",
skip_serializing_if = "is_empty_map"
)
)]
pub annotations: BTreeMap<String, serde_json::Value>,
}
impl Default for Metadata {
fn default() -> Self {
let _id = Uuid::new_v4();
Self {
_id,
name: _id.to_string(),
kind: "default".to_string(),
labels: BTreeSet::<String>::default(),
weights: BTreeMap::<String, f64>::default(),
annotations: BTreeMap::<String, serde_json::Value>::default(),
}
}
}
impl Metadata {
pub fn new(
id: Option<Uuid>,
name: Option<String>,
kind: Option<String>,
labels: Option<BTreeSet<String>>,
weights: Option<BTreeMap<String, f64>>,
annotations: Option<BTreeMap<String, serde_json::Value>>,
) -> Self {
let _id = match id {
None => Uuid::new_v4(),
Some(x) => x,
};
Self {
_id,
name: name.unwrap_or_else(|| _id.to_string()),
kind: kind.unwrap_or_else(|| "default".to_string()),
labels: labels.unwrap_or_default(),
weights: weights.unwrap_or_default(),
annotations: annotations.unwrap_or_default(),
}
}
pub fn id(&self) -> &Uuid {
&self._id
}
pub fn set_id(&mut self, value: Option<Uuid>) {
self._id = value.unwrap_or_else(Uuid::new_v4);
}
}
pub trait Meta {
fn get_meta(&self) -> &Metadata;
fn id(&self) -> &Uuid {
&self.get_meta()._id
}
fn name(&self) -> &String {
&self.get_meta().name
}
fn kind(&self) -> &String {
&self.get_meta().kind
}
fn labels(&self) -> &BTreeSet<String> {
&self.get_meta().labels
}
fn weights(&self) -> &BTreeMap<String, f64> {
&self.get_meta().weights
}
fn annotations(&self) -> &BTreeMap<String, serde_json::Value> {
&self.get_meta().annotations
}
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Aggregator {
#[default]
Binary,
Kinds,
Labels,
Weights,
Annotations,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Aggregate {
pub items: isize,
pub kinds: BTreeMap<String, isize>,
pub labels: BTreeMap<String, isize>,
pub weights: BTreeMap<String, f64>,
pub annotations: BTreeMap<String, isize>,
}
impl Aggregate {
pub fn new(data: &[&Metadata]) -> Self {
let mut agg = Self::default();
for &d in data.iter() {
agg.add(d)
}
agg
}
pub fn add(&mut self, item: &Metadata) {
self.items += 1;
*self.kinds.entry(item.kind.clone()).or_insert(0) += 1;
for label in item.labels.iter() {
*self.labels.entry(label.clone()).or_insert(0) += 1;
}
for (key, value) in item.weights.iter() {
*self.weights.entry(key.clone()).or_insert(0.0) += value;
}
for key in item.annotations.keys() {
*self.annotations.entry(key.clone()).or_insert(0) += 1;
}
}
pub fn subtract(&mut self, item: &Metadata) {
self.items -= 1;
if let Some(&num) = self.kinds.get(&item.kind) {
if num <= 1 {
self.kinds.remove(&item.kind);
} else {
*self.kinds.entry(item.kind.clone()).or_insert(1) -= 1;
}
}
for label in item.labels.iter() {
if let Some(&num) = self.labels.get(label) {
if num <= 1 {
self.labels.remove(label);
} else {
*self.labels.entry(label.clone()).or_insert(1) -= 1;
}
}
}
for (key, value) in item.weights.iter() {
*self.weights.entry(key.clone()).or_insert(0.0) -= value;
}
for key in item.annotations.keys() {
if let Some(&num) = self.annotations.get(key) {
if num <= 1 {
self.annotations.remove(key);
} else {
*self.annotations.entry(key.clone()).or_insert(1) -= 1;
}
}
}
}
pub fn extend(&mut self, other: Self) -> &Self {
self.items += other.items;
for (key, value) in other.kinds {
*self.kinds.entry(key).or_insert(0) += value;
}
for (key, value) in other.labels {
*self.labels.entry(key).or_insert(0) += value;
}
for (key, value) in other.weights {
*self.weights.entry(key).or_insert(0.0) += value;
}
for (key, value) in other.annotations {
*self.annotations.entry(key).or_insert(0) += value;
}
self
}
pub fn sum(
&self,
aggregator: &Aggregator,
fields: Option<&BTreeSet<String>>,
absolute: bool,
) -> f64 {
match aggregator {
Aggregator::Binary => {
if self.items > 0 {
1.0
} else {
0.0
}
}
Aggregator::Kinds => match fields {
None => self.kinds.values().sum::<isize>() as f64,
Some(fields) => fields
.iter()
.map(|field| self.value(aggregator, field, absolute))
.sum(),
},
Aggregator::Labels => match fields {
None => self.labels.values().sum::<isize>() as f64,
Some(fields) => fields
.iter()
.map(|field| self.value(aggregator, field, absolute))
.sum(),
},
Aggregator::Weights => match fields {
None => self.weights.values().sum(),
Some(fields) => fields
.iter()
.map(|field| self.value(aggregator, field, absolute))
.sum(),
},
Aggregator::Annotations => match fields {
None => self.annotations.values().sum::<isize>() as f64,
Some(fields) => fields
.iter()
.map(|field| self.value(aggregator, field, absolute))
.sum(),
},
}
}
pub fn value(&self, aggregator: &Aggregator, field: &String, absolute: bool) -> f64 {
let value = match aggregator {
Aggregator::Binary => 1.0,
Aggregator::Kinds => self.kinds.get(field).map(|&x| x as f64).unwrap_or(0.0),
Aggregator::Labels => self.labels.get(field).map(|&x| x as f64).unwrap_or(0.0),
Aggregator::Weights => self.weights.get(field).copied().unwrap_or(0.0),
Aggregator::Annotations => self
.annotations
.get(field)
.map(|&x| x as f64)
.unwrap_or(0.0),
};
if absolute {
value.abs()
} else {
value
}
}
pub fn fraction(
&self,
aggregator: &Aggregator,
field: &String,
fields: Option<&BTreeSet<String>>,
absolute: bool,
) -> f64 {
let sum = self.sum(aggregator, fields, absolute);
let value = self.value(aggregator, field, absolute);
if sum == 0.0 {
0.0
} else {
value / sum
}
}
pub fn fractions(
&self,
aggregator: &Aggregator,
fields: &Vec<String>,
factor: f64,
absolute: bool,
) -> Vec<f64> {
let all: BTreeSet<String> = fields.clone().into_iter().collect();
let sum = self.sum(aggregator, Some(&all), absolute);
let factor = {
if sum == 0.0 {
1.0
} else {
factor / sum
}
};
fields
.iter()
.map(|field| factor * self.value(aggregator, field, absolute))
.collect()
}
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Domains {
pub bounds: BTreeMap<String, (f64, f64)>,
}
impl Domains {
pub fn update(&mut self, values: &BTreeMap<String, f64>) {
for (key, &value) in values.iter() {
if let Some(entry) = self.bounds.get_mut(key) {
entry.0 = entry.0.min(value);
entry.1 = entry.1.max(value);
} else {
self.bounds.insert(key.to_owned(), (value, value));
}
}
}
pub fn get(&self, key: &String) -> Option<&(f64, f64)> {
self.bounds.get(key)
}
pub fn interpolate(&self, key: &String, value: f64) -> Option<f64> {
self.get(key).map(|&(lower, upper)| {
if lower == upper {
1.0
} else {
(value - lower) / (upper - lower)
}
})
}
}
#[derive(Clone, Default, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MetadataFilter {
pub kinds: Option<Vec<String>>,
pub labels: Option<BTreeSet<String>>,
}
impl MetadataFilter {
pub fn satisfies<T: Meta>(&self, instance: &T) -> bool {
self.satisfies_kinds(instance) && self.satisfies_labels(instance)
}
pub fn satisfies_kinds<T: Meta>(&self, instance: &T) -> bool {
if let Some(kinds) = &self.kinds {
kinds.contains(instance.kind())
} else {
true
}
}
pub fn satisfies_labels<T: Meta>(&self, instance: &T) -> bool {
{
if let Some(labels) = &self.labels {
!labels.is_disjoint(instance.labels())
} else {
true
}
}
}
}