use std::borrow::Cow;
use serde::Serialize;
const TEMPORALITY_CUMULATIVE: u8 = 2;
#[derive(Debug, Clone, Copy)]
pub(super) struct Nanos(u64);
impl Nanos {
pub(super) const fn new(value: u64) -> Self {
Self(value)
}
pub(super) const fn as_u64(self) -> u64 {
self.0
}
}
impl Serialize for Nanos {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(&self.0)
}
}
#[derive(Debug, Clone, Copy)]
struct IntStr(u64);
impl Serialize for IntStr {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(&self.0)
}
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct ExportRequest<'a> {
resource_metrics: Vec<ResourceMetrics<'a>>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ResourceMetrics<'a> {
resource: Resource<'a>,
scope_metrics: Vec<ScopeMetrics<'a>>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Resource<'a> {
attributes: Vec<KeyValue<'a>>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ScopeMetrics<'a> {
scope: Scope<'a>,
metrics: Vec<Metric<'a>>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Scope<'a> {
name: &'a str,
version: &'a str,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct Metric<'a> {
name: Cow<'a, str>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<Cow<'a, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
gauge: Option<Gauge<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
sum: Option<Sum<'a>>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Gauge<'a> {
data_points: Vec<DataPoint<'a>>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Sum<'a> {
data_points: Vec<DataPoint<'a>>,
aggregation_temporality: u8,
is_monotonic: bool,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct DataPoint<'a> {
attributes: Vec<KeyValue<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
start_time_unix_nano: Option<Nanos>,
time_unix_nano: Nanos,
#[serde(skip_serializing_if = "Option::is_none")]
as_int: Option<IntStr>,
#[serde(skip_serializing_if = "Option::is_none")]
as_double: Option<f64>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct KeyValue<'a> {
key: &'a str,
value: StringValue<'a>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct StringValue<'a> {
string_value: Cow<'a, str>,
}
pub(super) fn attr<'a>(key: &'a str, value: impl Into<Cow<'a, str>>) -> KeyValue<'a> {
KeyValue {
key,
value: StringValue {
string_value: value.into(),
},
}
}
pub(super) fn gauge_point<'a>(
attributes: Vec<KeyValue<'a>>,
now: Nanos,
value: u64,
) -> DataPoint<'a> {
DataPoint {
attributes,
start_time_unix_nano: None,
time_unix_nano: now,
as_int: Some(IntStr(value)),
as_double: None,
}
}
pub(super) fn double_point<'a>(
attributes: Vec<KeyValue<'a>>,
now: Nanos,
value: f64,
) -> DataPoint<'a> {
DataPoint {
attributes,
start_time_unix_nano: None,
time_unix_nano: now,
as_int: None,
as_double: Some(value),
}
}
pub(super) fn cumulative_point<'a>(
attributes: Vec<KeyValue<'a>>,
start: Nanos,
now: Nanos,
value: u64,
) -> DataPoint<'a> {
DataPoint {
attributes,
start_time_unix_nano: Some(start),
time_unix_nano: now,
as_int: Some(IntStr(value)),
as_double: None,
}
}
pub(super) fn cumulative_double_point<'a>(
attributes: Vec<KeyValue<'a>>,
start: Nanos,
now: Nanos,
value: f64,
) -> DataPoint<'a> {
DataPoint {
attributes,
start_time_unix_nano: Some(start),
time_unix_nano: now,
as_int: None,
as_double: Some(value),
}
}
pub(super) fn cumulative_count_point<'a>(
attributes: Vec<KeyValue<'a>>,
start: Nanos,
now: Nanos,
value: f64,
) -> DataPoint<'a> {
DataPoint {
attributes,
start_time_unix_nano: Some(start),
time_unix_nano: now,
as_int: Some(IntStr(value.max(0.0) as u64)),
as_double: None,
}
}
pub(super) fn gauge<'a>(
name: impl Into<Cow<'a, str>>,
description: Option<impl Into<Cow<'a, str>>>,
data_points: Vec<DataPoint<'a>>,
) -> Option<Metric<'a>> {
(!data_points.is_empty()).then_some(Metric {
name: name.into(),
description: description.map(Into::into),
gauge: Some(Gauge { data_points }),
sum: None,
})
}
pub(super) fn cumulative_sum<'a>(
name: impl Into<Cow<'a, str>>,
description: Option<impl Into<Cow<'a, str>>>,
data_points: Vec<DataPoint<'a>>,
) -> Option<Metric<'a>> {
(!data_points.is_empty()).then_some(Metric {
name: name.into(),
description: description.map(Into::into),
gauge: None,
sum: Some(Sum {
data_points,
aggregation_temporality: TEMPORALITY_CUMULATIVE,
is_monotonic: true,
}),
})
}
pub(super) fn export_request<'a>(
resource_attributes: Vec<KeyValue<'a>>,
scope_name: &'a str,
scope_version: &'a str,
metrics: Vec<Metric<'a>>,
) -> ExportRequest<'a> {
ExportRequest {
resource_metrics: vec![ResourceMetrics {
resource: Resource {
attributes: resource_attributes,
},
scope_metrics: vec![ScopeMetrics {
scope: Scope {
name: scope_name,
version: scope_version,
},
metrics,
}],
}],
}
}