use std::{
collections::HashMap,
fmt::{self, Write},
sync::Arc,
};
use auto_ops::impl_op_ex;
use crate::internal::{render_label_values, RenderableMetricValue};
pub type Timestamp = f64;
#[derive(Debug, Clone, PartialEq)]
pub struct Exemplar {
pub labels: HashMap<String, String>,
pub timestamp: Option<f64>,
pub id: f64,
}
impl Exemplar {
pub fn new(labels: HashMap<String, String>, id: f64, timestamp: Option<f64>) -> Exemplar {
Exemplar {
labels,
id,
timestamp,
}
}
}
impl fmt::Display for Exemplar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let names: Vec<&str> = self.labels.keys().map(|s| s.as_str()).collect();
let values: Vec<&str> = self.labels.keys().map(|s| s.as_str()).collect();
write!(f, "# {} {}", render_label_values(&names, &values), self.id)?;
if let Some(timestamp) = self.timestamp {
write!(f, " {}", format_float(timestamp))?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct MetricFamily<TypeSet, ValueType> {
pub family_name: String,
label_names: Arc<Vec<String>>,
pub family_type: TypeSet,
pub help: String,
pub unit: String,
metrics: Vec<Sample<ValueType>>,
}
impl<TypeSet, ValueType> MetricFamily<TypeSet, ValueType>
where
TypeSet: Clone,
ValueType: RenderableMetricValue + Clone,
{
pub fn new(
family_name: String,
label_names: Vec<String>,
family_type: TypeSet,
help: String,
unit: String,
) -> Self {
Self {
family_name,
label_names: Arc::new(label_names),
family_type,
help,
unit,
metrics: Vec::new(),
}
}
pub fn get_label_names(&self) -> &[String] {
return self.label_names.as_ref().as_slice();
}
pub fn clone_and_convert_type<T: RenderableMetricValue + Clone>(&self) -> MetricFamily<TypeSet, T> where T: From<ValueType> {
MetricFamily {
family_name: self.family_name.clone(),
label_names: Arc::new((*self.label_names).clone()),
family_type: self.family_type.clone(),
help: self.help.clone(),
unit: self.unit.clone(),
metrics: self
.metrics
.iter()
.map(|m| m.clone_with_new_value(m.value.clone().into()))
.collect(),
}
}
pub fn with_labels<'a, T>(&self, labels: T) -> Self
where
T: IntoIterator<Item = (&'a str, &'a str)>,
{
let mut label_names = self.label_names.as_ref().clone();
let mut samples = self.metrics.clone();
for (k, v) in labels {
match label_names.iter().position(|n| k == *n) {
Some(idx) => {
for sample in samples.iter_mut() {
sample.label_values[idx] = v.to_owned();
}
}
None => {
label_names.push(k.to_owned());
for sample in samples.iter_mut() {
sample.label_values.push(v.to_owned());
}
}
}
}
Self::new(
self.family_name.clone(),
label_names,
self.family_type.clone(),
self.help.clone(),
self.unit.clone(),
)
.with_samples(samples)
.unwrap()
}
pub fn without_label(&self, label_name: &str) -> Result<Self, ParseError> {
match self.label_names.iter().position(|n| n == label_name) {
Some(idx) => {
let mut label_names = self.label_names.as_ref().clone();
label_names.remove(idx);
let mut base = Self::new(
self.family_name.clone(),
label_names,
self.family_type.clone(),
self.help.clone(),
self.unit.clone(),
);
for sample in self.metrics.iter() {
let mut label_values = sample.label_values.clone();
label_values.remove(idx);
let new_sample =
Sample::new(label_values, sample.timestamp, sample.value.clone());
base.add_sample(new_sample)?;
}
Ok(base)
}
None => Err(ParseError::InvalidMetric(format!(
"No label `{}` in metric family",
label_name
))),
}
}
pub fn into_iter_samples(self) -> impl Iterator<Item = Sample<ValueType>> {
self.metrics.into_iter()
}
pub fn iter_samples(&self) -> impl Iterator<Item = &Sample<ValueType>> {
self.metrics.iter()
}
pub fn iter_samples_mut(&mut self) -> impl Iterator<Item = &mut Sample<ValueType>> {
self.metrics.iter_mut()
}
pub fn with_samples<T>(mut self, samples: T) -> Result<Self, ParseError>
where
T: IntoIterator<Item = Sample<ValueType>>,
{
for sample in samples {
self.add_sample(sample)?;
}
Ok(self)
}
pub fn get_sample_matches(&self, sample: &Sample<ValueType>) -> Option<&Sample<ValueType>> {
return self
.metrics
.iter()
.find(|&s| s.label_values == sample.label_values);
}
pub fn get_sample_matches_mut(
&mut self,
sample: &Sample<ValueType>,
) -> Option<&mut Sample<ValueType>> {
return self
.metrics
.iter_mut()
.find(|s| s.label_values == sample.label_values);
}
pub fn get_sample_by_label_values(
&self,
label_values: &[String],
) -> Option<&Sample<ValueType>> {
return self.metrics.iter().find(|s| s.label_values == label_values);
}
pub fn get_sample_by_label_values_mut(
&mut self,
label_values: &[String],
) -> Option<&mut Sample<ValueType>> {
return self
.metrics
.iter_mut()
.find(|s| s.label_values == label_values);
}
pub fn get_sample_by_labelset(&self, labelset: &LabelSet) -> Option<&Sample<ValueType>> {
return self.metrics.iter().find(|s| labelset.matches_sample(s));
}
pub fn get_sample_by_labelset_mut(
&mut self,
labelset: &LabelSet,
) -> Option<&mut Sample<ValueType>> {
return self.metrics.iter_mut().find(|s| labelset.matches_sample(s));
}
pub fn set_label(&mut self, label_name: &str, label_value: &str) -> Result<(), ParseError> {
let index = match self.label_names.iter().position(|s| s == label_name) {
Some(position) => position,
None => {
return Err(ParseError::ParseError(format!(
"No Label {} on Metric Family",
label_name
)));
}
};
for metric in self.metrics.iter_mut() {
if index == metric.label_values.len() {
metric.label_values.push(label_value.to_owned());
} else {
metric.label_values[index] = label_value.to_owned();
}
}
Ok(())
}
pub fn add_sample(&mut self, mut s: Sample<ValueType>) -> Result<(), ParseError> {
if s.label_values.len() != self.label_names.len() {
return Err(ParseError::InvalidMetric(format!(
"Cannot add a sample with {} labels into a family with {}",
s.label_values.len(),
self.label_names.len()
)));
}
if self.get_sample_by_label_values(&s.label_values).is_some() {
return Err(ParseError::InvalidMetric(format!(
"Cannot add a duplicate metric to a MetricFamily (Label Values: {:?})",
s.label_values
)));
}
s.set_label_names(self.label_names.clone());
self.metrics.push(s);
Ok(())
}
}
impl<TypeSet, ValueType> fmt::Display for MetricFamily<TypeSet, ValueType>
where
TypeSet: fmt::Display + Default + PartialEq,
ValueType: RenderableMetricValue + Clone,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.help.is_empty() {
writeln!(f, "# HELP {} {}", self.family_name, self.help)?;
}
if self.family_type != <TypeSet>::default() {
writeln!(f, "# TYPE {} {}", self.family_name, self.family_type)?;
}
if !self.unit.is_empty() {
writeln!(f, "# UNIT {} {}", self.family_name, self.unit)?;
}
let label_names: Vec<&str> = self.label_names.iter().map(|s| s.as_str()).collect();
for metric in self.metrics.iter() {
metric.render(f, &self.family_name, &label_names)?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct MetricsExposition<TypeSet, ValueType> {
pub families: HashMap<String, MetricFamily<TypeSet, ValueType>>,
}
impl<TypeSet, ValueType> fmt::Display for MetricsExposition<TypeSet, ValueType>
where
TypeSet: fmt::Display + Default + PartialEq,
ValueType: RenderableMetricValue + Clone,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, (_, family)) in self.families.iter().enumerate() {
write!(f, "{}", family)?;
if i != self.families.len()-1 {
write!(f, "\n")?;
}
}
Ok(())
}
}
impl<TypeSet, ValueType> Default for MetricsExposition<TypeSet, ValueType> {
fn default() -> Self {
Self::new()
}
}
impl<TypeSet, ValueType> MetricsExposition<TypeSet, ValueType> {
pub fn new() -> MetricsExposition<TypeSet, ValueType> {
MetricsExposition {
families: HashMap::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CounterValue {
pub value: MetricNumber,
pub created: Option<Timestamp>,
pub exemplar: Option<Exemplar>,
}
fn format_float(f: f64) -> String {
if f == f64::NEG_INFINITY {
String::from("-Inf")
}
else if f == f64::INFINITY {
String::from("+Inf")
}
else if f.is_nan() {
String::from("NaN")
}
else {
format!("{}", f)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct HistogramBucket {
pub count: MetricNumber,
pub upper_bound: f64,
pub exemplar: Option<Exemplar>,
}
impl RenderableMetricValue for HistogramBucket {
fn render(
&self,
f: &mut fmt::Formatter<'_>,
metric_name: &str,
_: Option<&Timestamp>,
label_names: &[&str],
label_values: &[&str],
) -> fmt::Result {
let upper_bound_str = format_float(self.upper_bound);
let label_names = {
let mut names = Vec::from(label_names);
names.push("le");
names
};
let label_values = {
let mut values = Vec::from(label_values);
values.push(&upper_bound_str);
values
};
write!(
f,
"{}_bucket{} {}",
metric_name,
render_label_values(&label_names, &label_values),
self.count
)?;
if let Some(ex) = self.exemplar.as_ref() {
write!(f, "{}", ex)?;
}
f.write_char('\n')?;
Ok(())
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct HistogramValue {
pub sum: Option<MetricNumber>,
pub count: Option<u64>,
pub created: Option<Timestamp>,
pub buckets: Vec<HistogramBucket>,
}
impl RenderableMetricValue for HistogramValue {
fn render(
&self,
f: &mut fmt::Formatter<'_>,
metric_name: &str,
timestamp: Option<&Timestamp>,
label_names: &[&str],
label_values: &[&str],
) -> fmt::Result {
for bucket in self.buckets.iter() {
bucket.render(f, metric_name, timestamp, label_names, label_values)?;
}
let labels = render_label_values(label_names, label_values);
if let Some(s) = self.sum {
writeln!(f, "{}_sum{} {}", metric_name, labels, s)?;
}
if let Some(c) = self.count {
writeln!(f, "{}_count{} {}", metric_name, labels, c)?;
}
if let Some(c) = self.created {
writeln!(f, "{}_created{} {}", metric_name, labels, c)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct State {
pub name: String,
pub enabled: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Quantile {
pub quantile: f64,
pub value: MetricNumber,
}
impl RenderableMetricValue for Quantile {
fn render(
&self,
f: &mut fmt::Formatter<'_>,
metric_name: &str,
_: Option<&Timestamp>,
label_names: &[&str],
label_values: &[&str],
) -> fmt::Result {
let quantile_str = format_float(self.quantile);
let label_names = {
let mut names = Vec::from(label_names);
names.push("quantile");
names
};
let label_values = {
let mut values = Vec::from(label_values);
values.push(&quantile_str);
values
};
writeln!(
f,
"{}{} {}",
metric_name,
render_label_values(&label_names, &label_values),
self.value
)
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct SummaryValue {
pub sum: Option<MetricNumber>,
pub count: Option<u64>,
pub created: Option<Timestamp>,
pub quantiles: Vec<Quantile>,
}
impl RenderableMetricValue for SummaryValue {
fn render(
&self,
f: &mut fmt::Formatter<'_>,
metric_name: &str,
timestamp: Option<&Timestamp>,
label_names: &[&str],
label_values: &[&str],
) -> fmt::Result {
for q in self.quantiles.iter() {
q.render(f, metric_name, timestamp, label_names, label_values)?;
}
let labels = render_label_values(label_names, label_values);
if let Some(s) = self.sum {
writeln!(f, "{}_sum{} {}", metric_name, labels, s)?;
}
if let Some(s) = self.count {
writeln!(f, "{}_count{} {}", metric_name, labels, s)?;
}
if let Some(s) = self.created {
writeln!(f, "{}_created{} {}", metric_name, labels, s)?;
}
Ok(())
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum OpenMetricsType {
Counter,
Gauge,
Histogram,
GaugeHistogram,
StateSet,
Summary,
Info,
Unknown,
}
#[derive(Debug, Clone)]
pub enum OpenMetricsValue {
Unknown(MetricNumber),
Gauge(MetricNumber),
Counter(CounterValue),
Histogram(HistogramValue),
StateSet(MetricNumber),
GaugeHistogram(HistogramValue),
Info,
Summary(SummaryValue),
}
impl RenderableMetricValue for OpenMetricsValue {
fn render(
&self,
f: &mut fmt::Formatter<'_>,
metric_name: &str,
timestamp: Option<&Timestamp>,
label_names: &[&str],
label_values: &[&str],
) -> fmt::Result {
let timestamp_str = timestamp.map(|t| format!(" {}", format_float(*t))).unwrap_or_default();
match self {
OpenMetricsValue::Unknown(n)
| OpenMetricsValue::Gauge(n)
| OpenMetricsValue::StateSet(n) =>{
writeln!(
f,
"{}{} {}{}",
metric_name,
render_label_values(label_names, label_values),
n,
timestamp_str
)
},
OpenMetricsValue::Counter(c) => {
write!(
f,
"{}{} {}{}",
metric_name,
render_label_values(label_names, label_values),
c.value,
timestamp_str
)?;
if let Some(ex) = c.exemplar.as_ref() {
write!(f, "{}", ex)?;
}
f.write_char('\n')
}
OpenMetricsValue::Histogram(h) | OpenMetricsValue::GaugeHistogram(h) => {
h.render(f, metric_name, timestamp, label_names, label_values)
}
OpenMetricsValue::Summary(s) => {
s.render(f, metric_name, timestamp, label_names, label_values)
}
OpenMetricsValue::Info => {
writeln!(
f,
"{}{} {} {}",
metric_name,
render_label_values(label_names, label_values),
MetricNumber::Int(1),
timestamp_str
)
}
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum PrometheusType {
Counter,
Gauge,
Histogram,
Summary,
Unknown,
}
impl fmt::Display for PrometheusType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let out = match self {
PrometheusType::Counter => "counter",
PrometheusType::Gauge => "gauge",
PrometheusType::Histogram => "histogram",
PrometheusType::Summary => "summary",
PrometheusType::Unknown => "unknown",
};
f.write_str(out)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PrometheusCounterValue {
pub value: MetricNumber,
pub exemplar: Option<Exemplar>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PrometheusValue {
Unknown(MetricNumber),
Gauge(MetricNumber),
Counter(PrometheusCounterValue),
Histogram(HistogramValue),
Summary(SummaryValue),
}
impl RenderableMetricValue for PrometheusValue {
fn render(
&self,
f: &mut fmt::Formatter<'_>,
metric_name: &str,
timestamp: Option<&Timestamp>,
label_names: &[&str],
label_values: &[&str],
) -> fmt::Result {
let timestamp_str = timestamp.map(|t| format!(" {}", format_float(*t))).unwrap_or_default();
match self {
PrometheusValue::Unknown(n) | PrometheusValue::Gauge(n) => writeln!(
f,
"{}{} {}{}",
metric_name,
render_label_values(label_names, label_values),
n,
timestamp_str
),
PrometheusValue::Counter(c) => {
write!(
f,
"{}{} {}{}",
metric_name,
render_label_values(label_names, label_values),
c.value,
timestamp_str
)?;
if let Some(ex) = c.exemplar.as_ref() {
write!(f, "{}", ex)?;
}
f.write_char('\n')
}
PrometheusValue::Histogram(h) => {
h.render(f, metric_name, timestamp, label_names, label_values)
}
PrometheusValue::Summary(s) => {
s.render(f, metric_name, timestamp, label_names, label_values)
}
}
}
}
#[derive(Debug, Clone)]
pub struct Sample<ValueType> {
label_names: Option<Arc<Vec<String>>>,
label_values: Vec<String>,
pub timestamp: Option<Timestamp>,
pub value: ValueType,
}
impl<ValueType> Sample<ValueType>
where
ValueType: RenderableMetricValue + Clone,
{
pub fn new(label_values: Vec<String>, timestamp: Option<Timestamp>, value: ValueType) -> Self {
Self {
label_values,
timestamp,
value,
label_names: None,
}
}
fn clone_with_new_value<T>(&self, value: T) -> Sample<T> where T: RenderableMetricValue + Clone {
return Sample {
label_names: self.label_names.clone(),
label_values: self.label_values.clone(),
timestamp: self.timestamp.clone(),
value,
}
}
fn set_label_names(&mut self, label_names: Arc<Vec<String>>) {
self.label_names = Some(label_names);
}
pub fn without_label(&self, label_name: &str) -> Result<Self, ParseError> {
if let Some(labels) = &self.label_names {
if let Some(idx) = labels.iter().position(|name| name == label_name) {
let mut label_values = self.label_values.clone();
label_values.remove(idx);
return Ok(Self::new(label_values, self.timestamp.clone(), self.value.clone()));
}
return Err(ParseError::InvalidMetric(format!("Label {} doesn't existin in metric", label_name)));
}
return Err(ParseError::InvalidMetric(format!("Metric isn't bound to a family, so doesn't have names")));
}
pub fn get_labelset(&self) -> Result<LabelSet, ParseError> {
if let Some(label_names) = &self.label_names {
return LabelSet::new(label_names.clone(), self);
}
Err(ParseError::InvalidMetric(
"Metric has not been bound to a family yet, and thus doesn't have label names"
.to_string(),
))
}
fn render(
&self,
f: &mut fmt::Formatter<'_>,
metric_name: &str,
label_names: &[&str],
) -> fmt::Result {
let values: Vec<&str> = self.label_values.iter().map(|s| s.as_str()).collect();
self.value.render(
f,
metric_name,
self.timestamp.as_ref(),
label_names,
&values,
)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MetricNumber {
Float(f64),
Int(i64),
}
impl fmt::Display for MetricNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MetricNumber::Float(n) => write!(f, "{}", format_float(*n)),
MetricNumber::Int(n) => write!(f, "{}", n),
}
}
}
impl MetricNumber {
pub fn as_f64(&self) -> f64 {
match self {
MetricNumber::Int(i) => *i as f64,
MetricNumber::Float(f) => *f,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
MetricNumber::Int(i) => Some(*i),
MetricNumber::Float(f) if (f.round() - *f).abs() < f64::EPSILON => Some(*f as i64),
_ => None,
}
}
}
impl_op_ex!(+ |a: &MetricNumber, b: &MetricNumber| -> MetricNumber {
match (a, b) {
(MetricNumber::Float(f), MetricNumber::Float(f2)) => MetricNumber::Float(f + f2),
(MetricNumber::Float(f), MetricNumber::Int(i)) => MetricNumber::Float(f + *i as f64),
(MetricNumber::Int(i), MetricNumber::Float(f)) => MetricNumber::Float(f + *i as f64),
(MetricNumber::Int(i), MetricNumber::Int(i2)) => MetricNumber::Int(i + i2),
}
});
impl_op_ex!(+= |a: &mut MetricNumber, b: &MetricNumber| {
match a {
MetricNumber::Float(f) => *f += b.as_f64(),
MetricNumber::Int(i) => *i += b.as_i64().unwrap(),
}
});
impl_op_ex!(-|a: &MetricNumber, b: &MetricNumber| -> MetricNumber {
match (a, b) {
(MetricNumber::Float(f), MetricNumber::Float(f2)) => MetricNumber::Float(f - f2),
(MetricNumber::Float(f), MetricNumber::Int(i)) => MetricNumber::Float(f - *i as f64),
(MetricNumber::Int(i), MetricNumber::Float(f)) => MetricNumber::Float(f - *i as f64),
(MetricNumber::Int(i), MetricNumber::Int(i2)) => MetricNumber::Int(i - i2),
}
});
impl_op_ex!(-= |a: &mut MetricNumber, b: &MetricNumber| {
match a {
MetricNumber::Float(f) => *f -= b.as_f64(),
MetricNumber::Int(i) => *i -= b.as_i64().unwrap(),
}
});
impl_op_ex!(*|a: &MetricNumber, b: &MetricNumber| -> MetricNumber {
match (a, b) {
(MetricNumber::Float(f), MetricNumber::Float(f2)) => MetricNumber::Float(f * f2),
(MetricNumber::Float(f), MetricNumber::Int(i)) => MetricNumber::Float(f * *i as f64),
(MetricNumber::Int(i), MetricNumber::Float(f)) => MetricNumber::Float(f * *i as f64),
(MetricNumber::Int(i), MetricNumber::Int(i2)) => MetricNumber::Int(i * i2),
}
});
impl_op_ex!(*= |a: &mut MetricNumber, b: &MetricNumber| {
match a {
MetricNumber::Float(f) => *f *= b.as_f64(),
MetricNumber::Int(i) => *i *= b.as_i64().unwrap(),
}
});
impl_op_ex!(/ |a: &MetricNumber, b: &MetricNumber| -> MetricNumber {
match (a, b) {
(MetricNumber::Float(f), MetricNumber::Float(f2)) => MetricNumber::Float(f / f2),
(MetricNumber::Float(f), MetricNumber::Int(i)) => MetricNumber::Float(f / *i as f64),
(MetricNumber::Int(i), MetricNumber::Float(f)) => MetricNumber::Float(f / *i as f64),
(MetricNumber::Int(i), MetricNumber::Int(i2)) => MetricNumber::Int(i / i2),
}
});
impl_op_ex!(/= |a: &mut MetricNumber, b: &MetricNumber| {
match a {
MetricNumber::Float(f) => *f /= b.as_f64(),
MetricNumber::Int(i) => *i /= b.as_i64().unwrap(),
}
});
#[derive(Debug)]
pub enum ParseError {
ParseError(String),
DuplicateMetric,
InvalidMetric(String),
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::ParseError(e) => e.fmt(f),
ParseError::DuplicateMetric => f.write_str("Found two metrics with the same labelset"),
ParseError::InvalidMetric(s) => f.write_str(s),
}
}
}
pub struct LabelSet<'a> {
label_names: Arc<Vec<String>>,
label_values: &'a [String],
}
impl<'a> LabelSet<'a> {
pub fn new<ValueType>(
label_names: Arc<Vec<String>>,
sample: &'a Sample<ValueType>,
) -> Result<Self, ParseError> {
if label_names.len() != sample.label_values.len() {
return Err(ParseError::InvalidMetric(format!(
"Cannot create labelset from family with {} labels and sample with {}",
label_names.len(),
sample.label_values.len()
)));
}
Ok(Self {
label_names,
label_values: &sample.label_values,
})
}
pub fn matches_sample<ValueType>(&self, sample: &Sample<ValueType>) -> bool {
self.matches_values(&sample.label_values)
}
pub fn matches_values(&self, label_values: &[String]) -> bool {
self.label_values == label_values
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &String)> {
return self.label_names.iter().zip(self.label_values);
}
pub fn iter_names(&self) -> impl Iterator<Item = &String> {
self.label_names.iter()
}
pub fn iter_values(&self) -> impl Iterator<Item = &String> {
self.label_values.iter()
}
pub fn get_label_value(&self, label_name: &str) -> Option<&str> {
return self
.label_names
.iter()
.position(|s| s == label_name)
.map(|i| self.label_values[i].as_str());
}
}