use core::fmt;
use fxhash::FxBuildHasher;
use ordered_float::OrderedFloat;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::hash::{Hash, Hasher};
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
use std::{collections::HashMap, fmt::Display};
use crate::metrics::def::{RawMetricId, TypedMetricId};
use crate::resources::ResourceConsumer;
use super::resources::Resource;
#[derive(Clone, Debug)]
pub struct MeasurementPoint {
pub metric: RawMetricId,
pub timestamp: Timestamp,
pub value: WrappedMeasurementValue,
pub resource: Resource,
pub consumer: ResourceConsumer,
attributes: SmallVec<[(Cow<'static, str>, AttributeValue); 4]>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Timestamp(pub(crate) SystemTime);
impl MeasurementPoint {
pub fn new<T: MeasurementType>(
timestamp: Timestamp,
metric: TypedMetricId<T>,
resource: Resource,
consumer: ResourceConsumer,
value: T::T,
) -> MeasurementPoint {
Self::new_untyped(timestamp, metric.0, resource, consumer, T::wrapped_value(value))
}
pub fn new_untyped(
timestamp: Timestamp,
metric: RawMetricId,
resource: Resource,
consumer: ResourceConsumer,
value: WrappedMeasurementValue,
) -> MeasurementPoint {
MeasurementPoint {
metric,
timestamp,
value,
resource,
consumer,
attributes: SmallVec::new(),
}
}
pub fn attributes_len(&self) -> usize {
self.attributes.len()
}
pub fn attributes(&self) -> impl Iterator<Item = (&str, &AttributeValue)> {
self.attributes.iter().map(|(k, v)| (k.as_ref(), v))
}
pub fn attributes_keys(&self) -> impl Iterator<Item = &str> {
self.attributes.iter().map(|(k, _v)| k.as_ref())
}
pub fn add_attr<K: Into<Cow<'static, str>>, V: Into<AttributeValue>>(&mut self, key: K, value: V) {
self.attributes.push((key.into(), value.into()));
}
pub fn with_attr<K: Into<Cow<'static, str>>, V: Into<AttributeValue>>(mut self, key: K, value: V) -> Self {
self.add_attr(key, value);
self
}
pub fn with_attr_vec<K: Into<Cow<'static, str>>>(mut self, attributes: Vec<(K, AttributeValue)>) -> Self {
self.attributes
.extend(attributes.into_iter().map(|(k, v)| (k.into(), v)));
self
}
pub fn with_attr_map<K: Into<Cow<'static, str>>>(
mut self,
attributes: HashMap<K, AttributeValue, FxBuildHasher>,
) -> Self {
let converted = attributes.into_iter().map(|(k, v)| (k.into(), v));
if self.attributes.is_empty() {
self.attributes = converted.collect();
} else {
self.attributes.extend(converted);
}
self
}
}
impl Timestamp {
pub fn now() -> Self {
Self(SystemTime::now())
}
pub fn to_unix_timestamp(&self) -> (u64, u32) {
let t = self.0.duration_since(UNIX_EPOCH).unwrap();
(t.as_secs(), t.subsec_nanos())
}
pub fn duration_since(&self, earlier: Timestamp) -> Result<Duration, SystemTimeError> {
self.0.duration_since(earlier.0)
}
}
impl From<SystemTime> for Timestamp {
fn from(value: SystemTime) -> Self {
Self(value)
}
}
impl From<Timestamp> for SystemTime {
fn from(value: Timestamp) -> Self {
value.0
}
}
impl fmt::Debug for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub trait MeasurementType {
type T;
fn wrapped_value(v: Self::T) -> WrappedMeasurementValue;
fn wrapped_type() -> WrappedMeasurementType;
}
impl MeasurementType for u64 {
type T = u64;
fn wrapped_value(v: Self::T) -> WrappedMeasurementValue {
WrappedMeasurementValue::U64(v)
}
fn wrapped_type() -> WrappedMeasurementType {
WrappedMeasurementType::U64
}
}
impl MeasurementType for f64 {
type T = f64;
fn wrapped_value(v: Self::T) -> WrappedMeasurementValue {
WrappedMeasurementValue::F64(v)
}
fn wrapped_type() -> WrappedMeasurementType {
WrappedMeasurementType::F64
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(C)]
pub enum WrappedMeasurementType {
F64,
U64,
}
impl fmt::Display for WrappedMeasurementType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum WrappedMeasurementValue {
F64(f64),
U64(u64),
}
impl WrappedMeasurementValue {
pub fn measurement_type(&self) -> WrappedMeasurementType {
match self {
WrappedMeasurementValue::F64(_) => WrappedMeasurementType::F64,
WrappedMeasurementValue::U64(_) => WrappedMeasurementType::U64,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AttributeValue {
F64(f64),
U64(u64),
Bool(bool),
Str(&'static str),
String(String),
}
impl Hash for AttributeValue {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
AttributeValue::F64(f64_value) => OrderedFloat(*f64_value).hash(state),
AttributeValue::Bool(bool_value) => bool_value.hash(state),
AttributeValue::U64(u64_value) => u64_value.hash(state),
AttributeValue::Str(str_value) => str_value.hash(state),
AttributeValue::String(string_value) => string_value.hash(state),
}
}
}
impl Eq for AttributeValue {}
impl Display for AttributeValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AttributeValue::F64(x) => write!(f, "{x}"),
AttributeValue::U64(x) => write!(f, "{x}"),
AttributeValue::Bool(x) => write!(f, "{x}"),
AttributeValue::Str(str) => f.write_str(str),
AttributeValue::String(str) => f.write_str(str),
}
}
}
impl From<f64> for AttributeValue {
fn from(value: f64) -> Self {
AttributeValue::F64(value)
}
}
impl From<u64> for AttributeValue {
fn from(value: u64) -> Self {
AttributeValue::U64(value)
}
}
impl From<bool> for AttributeValue {
fn from(value: bool) -> Self {
AttributeValue::Bool(value)
}
}
impl From<String> for AttributeValue {
fn from(value: String) -> Self {
AttributeValue::String(value)
}
}
impl From<&'static str> for AttributeValue {
fn from(value: &'static str) -> Self {
AttributeValue::Str(value)
}
}
#[derive(Clone, Debug)]
pub struct MeasurementBuffer {
points: Vec<MeasurementPoint>,
}
impl MeasurementBuffer {
pub fn new() -> MeasurementBuffer {
MeasurementBuffer { points: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> MeasurementBuffer {
MeasurementBuffer {
points: Vec::with_capacity(capacity),
}
}
pub fn is_empty(&self) -> bool {
self.points.is_empty()
}
pub fn len(&self) -> usize {
self.points.len()
}
pub fn reserve(&mut self, additional: usize) {
self.points.reserve(additional);
}
pub fn push(&mut self, point: MeasurementPoint) {
self.points.push(point);
}
pub fn merge(&mut self, other: &mut MeasurementBuffer) {
self.points.append(&mut other.points);
}
pub fn clear(&mut self) {
self.points.clear();
}
pub fn iter(&self) -> impl Iterator<Item = &MeasurementPoint> {
self.points.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut MeasurementPoint> {
self.points.iter_mut()
}
pub fn as_accumulator(&mut self) -> MeasurementAccumulator {
MeasurementAccumulator(self)
}
}
impl Default for MeasurementBuffer {
fn default() -> Self {
Self {
points: Default::default(),
}
}
}
impl<'a> IntoIterator for &'a MeasurementBuffer {
type Item = &'a MeasurementPoint;
type IntoIter = std::slice::Iter<'a, MeasurementPoint>;
fn into_iter(self) -> Self::IntoIter {
self.points.iter()
}
}
impl IntoIterator for MeasurementBuffer {
type Item = MeasurementPoint;
type IntoIter = std::vec::IntoIter<MeasurementPoint>;
fn into_iter(self) -> Self::IntoIter {
self.points.into_iter()
}
}
impl FromIterator<MeasurementPoint> for MeasurementBuffer {
fn from_iter<T: IntoIterator<Item = MeasurementPoint>>(iter: T) -> Self {
Self {
points: Vec::from_iter(iter),
}
}
}
impl From<Vec<MeasurementPoint>> for MeasurementBuffer {
fn from(value: Vec<MeasurementPoint>) -> Self {
MeasurementBuffer { points: value }
}
}
pub struct MeasurementAccumulator<'a>(&'a mut MeasurementBuffer);
impl<'a> MeasurementAccumulator<'a> {
pub fn push(&mut self, point: MeasurementPoint) {
self.0.push(point)
}
pub(crate) fn as_inner(&'a self) -> &'a MeasurementBuffer {
self.0
}
}