use std::collections::HashMap;
use crate::key::{KeyEncoder, LexicographicKey};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValueKind {
Bool,
I64,
F64,
String,
Distribution,
Tuple(Vec<ValueKind>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MetricKind {
Gauge,
Cumulative,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Field {
pub name: String,
pub kind: ValueKind,
}
impl Field {
pub fn new(name: impl Into<String>, kind: ValueKind) -> Self {
Self {
name: name.into(),
kind,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TargetSchema {
pub name: String,
pub fields: Vec<Field>,
pub location_field: String,
}
impl TargetSchema {
pub fn new(
name: impl Into<String>,
fields: Vec<Field>,
location_field: impl Into<String>,
) -> Self {
Self {
name: name.into(),
fields,
location_field: location_field.into(),
}
}
pub fn has_field(&self, name: &str) -> bool {
self.fields.iter().any(|field| field.name == name)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetricSchema {
pub name: String,
pub fields: Vec<Field>,
pub value_kind: ValueKind,
pub metric_kind: MetricKind,
}
impl MetricSchema {
pub fn new(
name: impl Into<String>,
fields: Vec<Field>,
value_kind: ValueKind,
metric_kind: MetricKind,
) -> Self {
Self {
name: name.into(),
fields,
value_kind,
metric_kind,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FieldValue {
Bool(bool),
I64(i64),
U64(u64),
F64(f64),
String(String),
}
impl LexicographicKey for FieldValue {
fn encode_key(&self, out: &mut Vec<u8>) {
match self {
Self::Bool(value) => value.encode_key(out),
Self::I64(value) => value.encode_key(out),
Self::U64(value) => value.encode_key(out),
Self::F64(value) => value.encode_key(out),
Self::String(value) => value.encode_key(out),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TimeSeriesKey {
pub target_schema: String,
pub target_fields: Vec<FieldValue>,
pub metric_name: String,
pub metric_fields: Vec<FieldValue>,
}
impl TimeSeriesKey {
pub fn target_key(&self) -> Vec<u8> {
let mut encoder = KeyEncoder::new();
encoder.push_field(self.target_schema.as_str());
for field in &self.target_fields {
encoder.push_field(field);
}
encoder.finish()
}
pub fn series_key(&self) -> Vec<u8> {
let mut encoder = KeyEncoder::new();
encoder.push_field(self.target_schema.as_str());
for field in &self.target_fields {
encoder.push_field(field);
}
encoder.push_field(self.metric_name.as_str());
for field in &self.metric_fields {
encoder.push_field(field);
}
encoder.finish()
}
}
#[derive(Debug, Clone, Default)]
pub struct LocationResolver {
zones: HashMap<String, String>,
}
impl LocationResolver {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, location: impl Into<String>, zone: impl Into<String>) {
self.zones.insert(location.into(), zone.into());
}
pub fn zone_for(&self, location: &str) -> Option<&str> {
self.zones.get(location).map(String::as_str)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn time_series_key_has_target_and_full_series_forms() {
let key = TimeSeriesKey {
target_schema: "ComputeTask".to_owned(),
target_fields: vec![FieldValue::String("aa".to_owned())],
metric_name: "/rpc/server/latency".to_owned(),
metric_fields: vec![FieldValue::String("Query".to_owned())],
};
assert!(key.target_key() < key.series_key());
}
#[test]
fn location_resolver_maps_locations_to_zones() {
let mut resolver = LocationResolver::new();
resolver.insert("aa", "zone-west");
assert_eq!(resolver.zone_for("aa"), Some("zone-west"));
}
}