openmetrics-parser 0.4.4

An OpenMetrics/Prometheus Text Exposition Format Parser
Documentation
use std::fmt;

use crate::{
    CounterValue, Exemplar, HistogramValue, MetricNumber, ParseError, PrometheusCounterValue,
    SummaryValue, Timestamp,
};

use super::MetricsType;

#[derive(Debug)]
pub enum MetricValueMarshal {
    Unknown(Option<MetricNumber>),
    Gauge(Option<MetricNumber>),
    Counter(CounterValueMarshal),
    Histogram(HistogramValue),
    StateSet(Option<MetricNumber>),
    GaugeHistogram(HistogramValue),
    Info,
    Summary(SummaryValue),
}

#[derive(Debug, Default)]
pub struct CounterValueMarshal {
    pub value: Option<MetricNumber>,
    pub created: Option<Timestamp>,
    pub exemplar: Option<Exemplar>,
}

impl From<CounterValueMarshal> for CounterValue {
    fn from(s: CounterValueMarshal) -> CounterValue {
        CounterValue {
            value: s.value.unwrap(),
            created: s.created,
            exemplar: s.exemplar,
        }
    }
}

impl From<CounterValueMarshal> for PrometheusCounterValue {
    fn from(s: CounterValueMarshal) -> PrometheusCounterValue {
        PrometheusCounterValue {
            value: s.value.unwrap(),
            exemplar: s.exemplar,
        }
    }
}

#[derive(Debug)]
pub struct MetricFamilyMarshal<T>
where
    T: MetricsType,
{
    pub name: Option<String>,
    pub label_names: Option<LabelNames<T>>,
    pub family_type: Option<T>,
    pub help: Option<String>,
    pub unit: Option<String>,
    pub metrics: Vec<MetricMarshal>,
    pub seen_label_sets: Vec<Vec<String>>,
    pub current_label_set: Option<Vec<String>>,
}

impl<T> MetricFamilyMarshal<T>
where
    T: MetricsType + Clone + Default + fmt::Debug,
{
    pub fn empty() -> MetricFamilyMarshal<T> {
        MetricFamilyMarshal {
            name: None,
            label_names: None,
            family_type: None,
            help: None,
            unit: None,
            metrics: Vec::new(),
            seen_label_sets: Vec::new(),
            current_label_set: None,
        }
    }

    pub fn get_metric_by_labelset_mut(
        &mut self,
        label_values: &[String],
    ) -> Option<&mut MetricMarshal> {
        return self
            .metrics
            .iter_mut()
            .find(|m| m.label_values == label_values);
    }

    pub fn add_metric(&mut self, metric: MetricMarshal) {
        self.metrics.push(metric);
    }

    pub fn try_set_label_names(
        &mut self,
        sample_name: &str,
        names: LabelNames<T>,
    ) -> Result<(), ParseError> {
        if self.label_names.is_none() {
            self.label_names = Some(names);
            return Ok(());
        }

        let old_names = self.label_names.as_ref().unwrap();
        if !old_names.matches(sample_name, &names) {
            return Err(ParseError::InvalidMetric(
                "Labels in metrics have different label sets".to_owned(),
            ));
        }

        Ok(())
    }

    pub fn set_or_test_name(&mut self, name: String) -> Result<(), ParseError> {
        let name = Some(name);
        if self.name.is_some() && self.name != name {
            return Err(ParseError::InvalidMetric(format!(
                "Invalid metric name in family. Family name is {}, but got a metric called {}",
                self.name.as_ref().unwrap(),
                name.as_ref().unwrap()
            )));
        }

        self.name = name;
        Ok(())
    }

    pub fn try_add_help(&mut self, help: String) -> Result<(), ParseError> {
        if self.help.is_some() {
            return Err(ParseError::InvalidMetric(
                "Got two help lines in the same metric family".to_string(),
            ));
        }

        self.help = Some(help);

        Ok(())
    }

    pub fn try_add_unit(&mut self, unit: String) -> Result<(), ParseError> {
        if unit.is_empty() {
            return Ok(());
        }

        if self.unit.is_some() {
            return Err(ParseError::InvalidMetric(
                "Got two unit lines in the same metric family".to_string(),
            ));
        }

        if !self
            .family_type
            .as_ref()
            .cloned()
            .unwrap_or_default()
            .can_have_units()
        {
            return Err(ParseError::InvalidMetric(format!(
                "{:?} metrics can't have units",
                self.family_type
            )));
        }

        self.unit = Some(unit);

        Ok(())
    }

    pub fn try_add_type(&mut self, family_type: T) -> Result<(), ParseError> {
        if self.family_type.is_some() {
            return Err(ParseError::InvalidMetric(
                "Got two type lines in the same metric family".to_string(),
            ));
        }

        self.family_type = Some(family_type);

        Ok(())
    }
}

#[derive(Debug)]
pub struct LabelNames<T>
where
    T: MetricsType,
{
    pub names: Vec<String>,
    pub metric_type: T,
}

impl<T> LabelNames<T>
where
    T: MetricsType,
{
    pub fn new(sample_name: &str, metric_type: T, labels: Vec<String>) -> LabelNames<T> {
        let ignored_labels = <T>::get_ignored_labels(&metric_type, sample_name);
        let names = labels
            .into_iter()
            .filter(|s| !ignored_labels.contains(&s.as_str()))
            .collect();

        LabelNames { names, metric_type }
    }

    pub fn matches(&self, sample_name: &str, other_labels: &LabelNames<T>) -> bool {
        let ignored_labels = <T>::get_ignored_labels(&self.metric_type, sample_name);
        for name in self.names.iter() {
            if !ignored_labels.contains(&name.as_str()) && !other_labels.names.contains(name) {
                return false;
            }
        }

        true
    }
}

#[derive(Debug)]
pub struct MetricMarshal {
    pub label_values: Vec<String>,
    pub timestamp: Option<Timestamp>,
    pub value: MetricValueMarshal,
}

impl MetricMarshal {
    pub fn new(
        label_values: Vec<String>,
        timestamp: Option<Timestamp>,
        value: MetricValueMarshal,
    ) -> MetricMarshal {
        MetricMarshal {
            label_values,
            timestamp,
            value,
        }
    }
}

pub struct MetricProcesser(pub Box<MetricProccessFunc>);

type MetricProccessFunc = dyn Fn(
    &mut MetricMarshal,
    MetricNumber,
    Vec<String>,
    Vec<String>,
    Option<Exemplar>,
    bool,
) -> Result<(), ParseError>;

impl MetricProcesser {
    pub fn new<F>(f: F) -> MetricProcesser
    where
        F: Fn(
                &mut MetricMarshal,
                MetricNumber,
                Vec<String>,
                Vec<String>,
                Option<Exemplar>,
                bool,
            ) -> Result<(), ParseError>
            + 'static,
    {
        MetricProcesser(Box::new(f))
    }
}