1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2014 The Prometheus Authors
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use crate::atomic64::{Atomic, Number};
use crate::desc::{Desc, Describer};
use crate::errors::{Error, Result};
use crate::proto::{Counter, Gauge, LabelPair, Metric, MetricFamily, MetricType};

/// `ValueType` is an enumeration of metric types that represent a simple value
/// for [`Counter`] and [`Gauge`].
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ValueType {
    Counter,
    Gauge,
}

impl ValueType {
    /// `metric_type` returns the corresponding proto metric type.
    pub fn metric_type(self) -> MetricType {
        match self {
            ValueType::Counter => MetricType::COUNTER,
            ValueType::Gauge => MetricType::GAUGE,
        }
    }
}

/// A generic metric for [`Counter`] and [`Gauge`].
/// Its effective type is determined by `ValueType`. This is a low-level
/// building block used by the library to back the implementations of
/// [`Counter`] and [`Gauge`].
#[derive(Debug)]
pub struct Value<P: Atomic> {
    pub desc: Desc,
    pub val: P,
    pub val_type: ValueType,
    pub label_pairs: Vec<LabelPair>,
}

impl<P: Atomic> Value<P> {
    pub fn new<D: Describer>(
        describer: &D,
        val_type: ValueType,
        val: P::T,
        label_values: &[&str],
    ) -> Result<Self> {
        let desc = describer.describe()?;
        let label_pairs = make_label_pairs(&desc, label_values)?;

        Ok(Self {
            desc,
            val: P::new(val),
            val_type,
            label_pairs,
        })
    }

    #[inline]
    pub fn get(&self) -> P::T {
        self.val.get()
    }

    #[inline]
    pub fn set(&self, val: P::T) {
        self.val.set(val);
    }

    #[inline]
    pub fn inc_by(&self, val: P::T) {
        self.val.inc_by(val);
    }

    #[inline]
    pub fn inc(&self) {
        self.inc_by(P::T::from_i64(1));
    }

    #[inline]
    pub fn dec(&self) {
        self.dec_by(P::T::from_i64(1));
    }

    #[inline]
    pub fn dec_by(&self, val: P::T) {
        self.val.dec_by(val)
    }

    pub fn metric(&self) -> Metric {
        let mut m = Metric::default();
        m.set_label(from_vec!(self.label_pairs.clone()));

        let val = self.get();
        match self.val_type {
            ValueType::Counter => {
                let mut counter = Counter::default();
                counter.set_value(val.into_f64());
                m.set_counter(counter);
            }
            ValueType::Gauge => {
                let mut gauge = Gauge::default();
                gauge.set_value(val.into_f64());
                m.set_gauge(gauge);
            }
        }

        m
    }

    pub fn collect(&self) -> MetricFamily {
        let mut m = MetricFamily::default();
        m.set_name(self.desc.fq_name.clone());
        m.set_help(self.desc.help.clone());
        m.set_field_type(self.val_type.metric_type());
        m.set_metric(from_vec!(vec![self.metric()]));
        m
    }
}

pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result<Vec<LabelPair>> {
    if desc.variable_labels.len() != label_values.len() {
        return Err(Error::InconsistentCardinality {
            expect: desc.variable_labels.len(),
            got: label_values.len(),
        });
    }

    let total_len = desc.variable_labels.len() + desc.const_label_pairs.len();
    if total_len == 0 {
        return Ok(vec![]);
    }

    if desc.variable_labels.is_empty() {
        return Ok(desc.const_label_pairs.clone());
    }

    let mut label_pairs = Vec::with_capacity(total_len);
    for (i, n) in desc.variable_labels.iter().enumerate() {
        let mut label_pair = LabelPair::default();
        label_pair.set_name(n.clone());
        label_pair.set_value(label_values[i].to_owned());
        label_pairs.push(label_pair);
    }

    for label_pair in &desc.const_label_pairs {
        label_pairs.push(label_pair.clone());
    }
    label_pairs.sort();
    Ok(label_pairs)
}