use std::error::Error;
use std::fmt::{self, Display, Formatter};
use lodestone_types::{
MetricAttributeV1, MetricAttributeValueV1, MetricDataV1, MetricExemplarV1, MetricExponentialBucketsV1,
MetricInstrumentationScopeV1, MetricNumberV1, MetricPointV1, MetricTemporalityV1,
};
use crate::{
I64, MetricAttribute, MetricAttributeValue, MetricData, MetricExemplar, MetricExponentialBuckets, MetricNumber,
MetricPoint, MetricTemporality, U64,
};
pub fn metric_point_from_wire(value: &MetricPoint) -> Result<MetricPointV1, NativeMetricWireError> {
Ok(MetricPointV1 {
format_version: u16::try_from(value.format_version).map_err(|_| NativeMetricWireError::Integer)?,
resource_attributes: metric_attributes_from_wire(&value.resource_attributes)?,
instrumentation_scope: MetricInstrumentationScopeV1 {
name: value.instrumentation_scope.name.clone(),
version: value.instrumentation_scope.version.clone(),
schema_url: value.instrumentation_scope.schema_url.clone(),
attributes: metric_attributes_from_wire(&value.instrumentation_scope.attributes)?,
},
metric_name: value.metric_name.clone(),
description: value.description.clone(),
unit: value.unit.clone(),
series_attributes: metric_attributes_from_wire(&value.series_attributes)?,
timestamp_unix_nanos: parse_u64(&value.timestamp_unix_nanos)?,
start_timestamp_unix_nanos: value.start_timestamp_unix_nanos.as_ref().map(parse_u64).transpose()?,
data: metric_data_from_wire(&value.data)?,
exemplars: value
.exemplars
.iter()
.map(metric_exemplar_from_wire)
.collect::<Result<Vec<_>, _>>()?,
})
}
fn metric_attributes_from_wire(values: &[MetricAttribute]) -> Result<Vec<MetricAttributeV1>, NativeMetricWireError> {
values
.iter()
.map(|attribute| {
Ok(MetricAttributeV1 {
key: attribute.key.clone(),
value: match &attribute.value {
MetricAttributeValue::String { value } => MetricAttributeValueV1::String { value: value.clone() },
MetricAttributeValue::Boolean { value } => MetricAttributeValueV1::Boolean { value: *value },
MetricAttributeValue::I64 { value } => MetricAttributeValueV1::I64 {
value: parse_i64(value)?,
},
MetricAttributeValue::U64 { value } => MetricAttributeValueV1::U64 {
value: parse_u64(value)?,
},
MetricAttributeValue::F64Bits { value } => MetricAttributeValueV1::F64Bits {
value: parse_u64(value)?,
},
},
})
})
.collect()
}
fn metric_number_from_wire(value: &MetricNumber) -> Result<MetricNumberV1, NativeMetricWireError> {
Ok(match value {
MetricNumber::I64 { value } => MetricNumberV1::I64 {
value: parse_i64(value)?,
},
MetricNumber::F64Bits { value } => MetricNumberV1::F64Bits {
value: parse_u64(value)?,
},
})
}
const fn metric_temporality_from_wire(value: MetricTemporality) -> MetricTemporalityV1 {
match value {
MetricTemporality::Cumulative => MetricTemporalityV1::Cumulative,
MetricTemporality::Delta => MetricTemporalityV1::Delta,
}
}
fn metric_exponential_buckets_from_wire(
value: &MetricExponentialBuckets,
) -> Result<MetricExponentialBucketsV1, NativeMetricWireError> {
Ok(MetricExponentialBucketsV1 {
offset: value.offset,
counts: value.counts.iter().map(parse_u64).collect::<Result<Vec<_>, _>>()?,
})
}
fn metric_data_from_wire(value: &MetricData) -> Result<MetricDataV1, NativeMetricWireError> {
Ok(match value {
MetricData::Gauge { value } => MetricDataV1::Gauge {
value: metric_number_from_wire(value)?,
},
MetricData::Sum {
value,
temporality,
monotonic,
} => MetricDataV1::Sum {
value: metric_number_from_wire(value)?,
temporality: metric_temporality_from_wire(*temporality),
monotonic: *monotonic,
},
MetricData::ExplicitHistogram {
temporality,
count,
sum,
bounds_f64_bits,
bucket_counts,
} => MetricDataV1::ExplicitHistogram {
temporality: metric_temporality_from_wire(*temporality),
count: parse_u64(count)?,
sum: sum.as_ref().map(metric_number_from_wire).transpose()?,
bounds_f64_bits: bounds_f64_bits.iter().map(parse_u64).collect::<Result<Vec<_>, _>>()?,
bucket_counts: bucket_counts.iter().map(parse_u64).collect::<Result<Vec<_>, _>>()?,
},
MetricData::ExponentialHistogram {
temporality,
count,
sum,
scale,
zero_count,
positive,
negative,
} => MetricDataV1::ExponentialHistogram {
temporality: metric_temporality_from_wire(*temporality),
count: parse_u64(count)?,
sum: sum.as_ref().map(metric_number_from_wire).transpose()?,
scale: *scale,
zero_count: parse_u64(zero_count)?,
positive: metric_exponential_buckets_from_wire(positive)?,
negative: metric_exponential_buckets_from_wire(negative)?,
},
})
}
fn metric_exemplar_from_wire(value: &MetricExemplar) -> Result<MetricExemplarV1, NativeMetricWireError> {
Ok(MetricExemplarV1 {
timestamp_unix_nanos: parse_u64(&value.timestamp_unix_nanos)?,
value: metric_number_from_wire(&value.value)?,
trace_id: value.trace_id.clone(),
span_id: value.span_id.clone(),
filtered_attributes: metric_attributes_from_wire(&value.filtered_attributes)?,
})
}
fn parse_u64(value: &U64) -> Result<u64, NativeMetricWireError> {
value.value.parse().map_err(|_| NativeMetricWireError::Integer)
}
fn parse_i64(value: &I64) -> Result<i64, NativeMetricWireError> {
value.value.parse().map_err(|_| NativeMetricWireError::Integer)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NativeMetricWireError {
Integer,
}
impl Display for NativeMetricWireError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str("native metric wire contains a noncanonical integer")
}
}
impl Error for NativeMetricWireError {}