Skip to main content

prometheus_client/encoding/
text.rs

1//! Open Metrics text format implementation.
2//!
3//! ```
4//! # use prometheus_client::encoding::text::{encode, encode_registry, encode_eof};
5//! # use prometheus_client::metrics::counter::Counter;
6//! # use prometheus_client::registry::Registry;
7//! #
8//! # // Create registry and counter and register the latter with the former.
9//! # let mut registry = Registry::default();
10//! # let counter: Counter = Counter::default();
11//! # registry.register(
12//! #   "my_counter",
13//! #   "This is my counter",
14//! #   counter.clone(),
15//! # );
16//! # counter.inc();
17//! let mut buffer = String::new();
18//!
19//! // Encode the complete OpenMetrics exposition into the message buffer
20//! encode(&mut buffer, &registry).unwrap();
21//! let expected_msg = "# HELP my_counter This is my counter.\n".to_owned() +
22//!                    "# TYPE my_counter counter\n" +
23//!                    "my_counter_total 1\n" +
24//!                    "# EOF\n";
25//! assert_eq!(expected_msg, buffer);
26//! buffer.clear();
27//!
28//! // Encode just the registry into the message buffer
29//! encode_registry(&mut buffer, &registry).unwrap();
30//! let expected_reg = "# HELP my_counter This is my counter.\n".to_owned() +
31//!                    "# TYPE my_counter counter\n" +
32//!                    "my_counter_total 1\n";
33//! assert_eq!(expected_reg, buffer);
34//!
35//! // Encode EOF marker into message buffer to complete the OpenMetrics exposition
36//! encode_eof(&mut buffer).unwrap();
37//! assert_eq!(expected_msg, buffer);
38//! ```
39
40use crate::encoding::{
41    EncodeExemplarTime, EncodeExemplarValue, EncodeLabelSet, NativeHistogram, NoLabelSet,
42};
43use crate::metrics::exemplar::Exemplar;
44use crate::metrics::MetricType;
45use crate::registry::{Prefix, Registry, Unit};
46
47use std::borrow::Cow;
48use std::collections::HashMap;
49use std::fmt::Write;
50
51/// Encode both the metrics registered with the provided [`Registry`] and the
52/// EOF marker into the provided [`Write`]r using the OpenMetrics text format.
53///
54/// Note: This function encodes the **complete** OpenMetrics exposition.
55///
56/// Use [`encode_registry`] or [`encode_eof`] if partial encoding is needed.
57///
58/// See [OpenMetrics exposition format](https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#text-format)
59/// for additional details.
60///
61/// # Examples
62///
63/// ```no_run
64/// # use prometheus_client::encoding::text::encode;
65/// # use prometheus_client::metrics::counter::Counter;
66/// # use prometheus_client::metrics::gauge::Gauge;
67/// # use prometheus_client::registry::Registry;
68/// #
69/// // Initialize registry with metric families
70/// let mut registry = Registry::default();
71/// let counter: Counter = Counter::default();
72/// registry.register(
73///     "my_counter",
74///     "This is my counter",
75///     counter.clone(),
76/// );
77/// let gauge: Gauge = Gauge::default();
78/// registry.register(
79///     "my_gauge",
80///     "This is my gauge",
81///     gauge.clone(),
82/// );
83///
84/// // Encode the complete OpenMetrics exposition into the buffer
85/// let mut buffer = String::new();
86/// encode(&mut buffer, &registry)?;
87/// # Ok::<(), std::fmt::Error>(())
88/// ```
89pub fn encode<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
90where
91    W: Write,
92{
93    encode_registry(writer, registry)?;
94    encode_eof(writer)
95}
96
97/// Encode the metrics registered with the provided [`Registry`] into the
98/// provided [`Write`]r using the OpenMetrics text format.
99///
100/// Note: The OpenMetrics exposition requires that a complete message must end
101/// with an EOF marker.
102///
103/// This function may be called repeatedly for the HTTP scrape response until
104/// [`encode_eof`] signals the end of the response.
105///
106/// This may also be used to compose a partial message with metrics assembled
107/// from multiple registries.
108///
109/// # Examples
110///
111/// ```no_run
112/// # use prometheus_client::encoding::text::encode_registry;
113/// # use prometheus_client::metrics::counter::Counter;
114/// # use prometheus_client::metrics::gauge::Gauge;
115/// # use prometheus_client::registry::Registry;
116/// #
117/// // Initialize registry with a counter
118/// let mut reg_counter = Registry::default();
119/// let counter: Counter = Counter::default();
120/// reg_counter.register(
121///     "my_counter",
122///     "This is my counter",
123///     counter.clone(),
124/// );
125///
126/// // Encode the counter registry into the buffer
127/// let mut buffer = String::new();
128/// encode_registry(&mut buffer, &reg_counter)?;
129///
130/// // Initialize another registry but with a gauge
131/// let mut reg_gauge = Registry::default();
132/// let gauge: Gauge = Gauge::default();
133/// reg_gauge.register(
134///   "my_gauge",
135///   "This is my gauge",
136///   gauge.clone(),
137/// );
138///
139/// // Encode the gauge registry into the buffer
140/// encode_registry(&mut buffer, &reg_gauge)?;
141/// # Ok::<(), std::fmt::Error>(())
142/// ```
143pub fn encode_registry<W>(writer: &mut W, registry: &Registry) -> Result<(), std::fmt::Error>
144where
145    W: Write,
146{
147    registry.encode(&mut DescriptorEncoder::new(writer).into())
148}
149
150/// Encode the EOF marker into the provided [`Write`]r using the OpenMetrics
151/// text format.
152///
153/// Note: This function is used to mark/signal the end of the exposition.
154///
155/// # Examples
156///
157/// ```no_run
158/// # use prometheus_client::encoding::text::{encode_registry, encode_eof};
159/// # use prometheus_client::metrics::counter::Counter;
160/// # use prometheus_client::metrics::gauge::Gauge;
161/// # use prometheus_client::registry::Registry;
162/// #
163/// // Initialize registry with a counter
164/// let mut registry = Registry::default();
165/// let counter: Counter = Counter::default();
166/// registry.register(
167///     "my_counter",
168///     "This is my counter",
169///     counter.clone(),
170/// );
171///
172/// // Encode registry into the buffer
173/// let mut buffer = String::new();
174/// encode_registry(&mut buffer, &registry)?;
175///
176/// // Encode EOF marker to complete the message
177/// encode_eof(&mut buffer)?;
178/// # Ok::<(), std::fmt::Error>(())
179/// ```
180pub fn encode_eof<W>(writer: &mut W) -> Result<(), std::fmt::Error>
181where
182    W: Write,
183{
184    writer.write_str("# EOF\n")
185}
186
187pub(crate) struct DescriptorEncoder<'a> {
188    writer: &'a mut dyn Write,
189    prefix: Option<&'a Prefix>,
190    labels: &'a [(Cow<'static, str>, Cow<'static, str>)],
191}
192
193impl std::fmt::Debug for DescriptorEncoder<'_> {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        f.debug_struct("DescriptorEncoder").finish()
196    }
197}
198
199impl DescriptorEncoder<'_> {
200    pub(crate) fn new(writer: &mut dyn Write) -> DescriptorEncoder<'_> {
201        DescriptorEncoder {
202            writer,
203            prefix: Default::default(),
204            labels: Default::default(),
205        }
206    }
207
208    pub(crate) fn with_prefix_and_labels<'s>(
209        &'s mut self,
210        prefix: Option<&'s Prefix>,
211        labels: &'s [(Cow<'static, str>, Cow<'static, str>)],
212    ) -> DescriptorEncoder<'s> {
213        DescriptorEncoder {
214            prefix,
215            labels,
216            writer: self.writer,
217        }
218    }
219
220    pub fn encode_descriptor<'s>(
221        &'s mut self,
222        name: &'s str,
223        help: &str,
224        unit: Option<&'s Unit>,
225        metric_type: MetricType,
226    ) -> Result<MetricEncoder<'s>, std::fmt::Error> {
227        self.writer.write_str("# HELP ")?;
228        if let Some(prefix) = self.prefix {
229            self.writer.write_str(prefix.as_str())?;
230            self.writer.write_str("_")?;
231        }
232        self.writer.write_str(name)?;
233        if let Some(unit) = unit {
234            self.writer.write_str("_")?;
235            self.writer.write_str(unit.as_str())?;
236        }
237        self.writer.write_str(" ")?;
238        self.writer.write_str(help)?;
239        self.writer.write_str("\n")?;
240
241        self.writer.write_str("# TYPE ")?;
242        if let Some(prefix) = self.prefix {
243            self.writer.write_str(prefix.as_str())?;
244            self.writer.write_str("_")?;
245        }
246        self.writer.write_str(name)?;
247        if let Some(unit) = unit {
248            self.writer.write_str("_")?;
249            self.writer.write_str(unit.as_str())?;
250        }
251        self.writer.write_str(" ")?;
252        self.writer.write_str(metric_type.as_str())?;
253        self.writer.write_str("\n")?;
254
255        if let Some(unit) = unit {
256            self.writer.write_str("# UNIT ")?;
257            if let Some(prefix) = self.prefix {
258                self.writer.write_str(prefix.as_str())?;
259                self.writer.write_str("_")?;
260            }
261            self.writer.write_str(name)?;
262            self.writer.write_str("_")?;
263            self.writer.write_str(unit.as_str())?;
264            self.writer.write_str(" ")?;
265            self.writer.write_str(unit.as_str())?;
266            self.writer.write_str("\n")?;
267        }
268
269        Ok(MetricEncoder {
270            writer: self.writer,
271            prefix: self.prefix,
272            name,
273            unit,
274            const_labels: self.labels,
275            family_labels: None,
276        })
277    }
278}
279
280/// Helper type for [`EncodeMetric`](super::EncodeMetric), see
281/// [`EncodeMetric::encode`](super::EncodeMetric::encode).
282///
283// `MetricEncoder` does not take a trait parameter for `writer` and `labels`
284// because `EncodeMetric` which uses `MetricEncoder` needs to be usable as a
285// trait object in order to be able to register different metric types with a
286// `Registry`. Trait objects can not use type parameters.
287//
288// TODO: Alternative solutions to the above are very much appreciated.
289pub(crate) struct MetricEncoder<'a> {
290    writer: &'a mut dyn Write,
291    prefix: Option<&'a Prefix>,
292    name: &'a str,
293    unit: Option<&'a Unit>,
294    const_labels: &'a [(Cow<'static, str>, Cow<'static, str>)],
295    family_labels: Option<&'a dyn super::EncodeLabelSet>,
296}
297
298impl std::fmt::Debug for MetricEncoder<'_> {
299    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300        let mut labels = String::new();
301        let mut encoder = LabelSetEncoder::new(&mut labels).into();
302        if let Some(l) = self.family_labels {
303            l.encode(&mut encoder)?;
304        }
305
306        f.debug_struct("Encoder")
307            .field("name", &self.name)
308            .field("prefix", &self.prefix)
309            .field("unit", &self.unit)
310            .field("const_labels", &self.const_labels)
311            .field("labels", &labels.as_str())
312            .finish()
313    }
314}
315
316impl MetricEncoder<'_> {
317    pub fn encode_counter<
318        S: EncodeLabelSet,
319        CounterValue: super::EncodeCounterValue,
320        ExemplarValue: EncodeExemplarValue,
321    >(
322        &mut self,
323        v: &CounterValue,
324        exemplar: Option<&Exemplar<S, ExemplarValue>>,
325    ) -> Result<(), std::fmt::Error> {
326        self.write_prefix_name_unit()?;
327
328        self.write_suffix("total")?;
329
330        self.encode_labels::<NoLabelSet>(None)?;
331
332        v.encode(
333            &mut CounterValueEncoder {
334                writer: self.writer,
335            }
336            .into(),
337        )?;
338
339        if let Some(exemplar) = exemplar {
340            self.encode_exemplar(exemplar)?;
341        }
342
343        self.newline()?;
344
345        Ok(())
346    }
347
348    pub fn encode_gauge<GaugeValue: super::EncodeGaugeValue>(
349        &mut self,
350        v: &GaugeValue,
351    ) -> Result<(), std::fmt::Error> {
352        self.write_prefix_name_unit()?;
353
354        self.encode_labels::<NoLabelSet>(None)?;
355
356        v.encode(
357            &mut GaugeValueEncoder {
358                writer: self.writer,
359            }
360            .into(),
361        )?;
362
363        self.newline()?;
364
365        Ok(())
366    }
367
368    pub fn encode_info<S: EncodeLabelSet>(&mut self, label_set: &S) -> Result<(), std::fmt::Error> {
369        self.write_prefix_name_unit()?;
370
371        self.write_suffix("info")?;
372
373        self.encode_labels(Some(label_set))?;
374
375        self.writer.write_str(" ")?;
376        self.writer.write_str(itoa::Buffer::new().format(1))?;
377
378        self.newline()?;
379
380        Ok(())
381    }
382
383    /// Encode a set of labels. Used by wrapper metric types like
384    /// [`Family`](crate::metrics::family::Family).
385    pub fn encode_family<'s, S: EncodeLabelSet>(
386        &'s mut self,
387        label_set: &'s S,
388    ) -> Result<MetricEncoder<'s>, std::fmt::Error> {
389        debug_assert!(self.family_labels.is_none());
390
391        Ok(MetricEncoder {
392            writer: self.writer,
393            prefix: self.prefix,
394            name: self.name,
395            unit: self.unit,
396            const_labels: self.const_labels,
397            family_labels: Some(label_set),
398        })
399    }
400
401    pub fn encode_histogram<S: EncodeLabelSet>(
402        &mut self,
403        sum: f64,
404        count: u64,
405        buckets: &[(f64, u64)],
406        exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
407    ) -> Result<(), std::fmt::Error> {
408        self.write_prefix_name_unit()?;
409        self.write_suffix("sum")?;
410        self.encode_labels::<NoLabelSet>(None)?;
411        self.writer.write_str(" ")?;
412        self.writer.write_str(dtoa::Buffer::new().format(sum))?;
413        self.newline()?;
414
415        self.write_prefix_name_unit()?;
416        self.write_suffix("count")?;
417        self.encode_labels::<NoLabelSet>(None)?;
418        self.writer.write_str(" ")?;
419        self.writer.write_str(itoa::Buffer::new().format(count))?;
420        self.newline()?;
421
422        let mut cummulative = 0;
423        for (i, (upper_bound, count)) in buckets.iter().enumerate() {
424            cummulative += count;
425
426            self.write_prefix_name_unit()?;
427            self.write_suffix("bucket")?;
428
429            if *upper_bound == f64::MAX {
430                self.encode_labels(Some(&[("le", "+Inf")]))?;
431            } else {
432                self.encode_labels(Some(&[("le", *upper_bound)]))?;
433            }
434
435            self.writer.write_str(" ")?;
436            self.writer
437                .write_str(itoa::Buffer::new().format(cummulative))?;
438
439            if let Some(exemplar) = exemplars.and_then(|e| e.get(&i)) {
440                self.encode_exemplar(exemplar)?
441            }
442
443            self.newline()?;
444        }
445
446        Ok(())
447    }
448
449    pub fn encode_histogram_with_native<S: EncodeLabelSet>(
450        &mut self,
451        sum: f64,
452        count: u64,
453        buckets: &[(f64, u64)],
454        exemplars: Option<&HashMap<usize, Exemplar<S, f64>>>,
455        _native: NativeHistogram<'_>,
456    ) -> Result<(), std::fmt::Error> {
457        if buckets.is_empty() {
458            return Err(std::fmt::Error);
459        }
460
461        self.encode_histogram(sum, count, buckets, exemplars)
462    }
463
464    /// Encode an exemplar for the given metric.
465    fn encode_exemplar<S: EncodeLabelSet, V: EncodeExemplarValue>(
466        &mut self,
467        exemplar: &Exemplar<S, V>,
468    ) -> Result<(), std::fmt::Error> {
469        self.writer.write_str(" # {")?;
470        exemplar
471            .label_set
472            .encode(&mut LabelSetEncoder::new(self.writer).into())?;
473        self.writer.write_str("} ")?;
474        exemplar.value.encode(
475            ExemplarValueEncoder {
476                writer: self.writer,
477            }
478            .into(),
479        )?;
480        if let Some(timestamp) = exemplar.timestamp {
481            self.writer.write_char(' ')?;
482            timestamp.encode(
483                ExemplarValueEncoder {
484                    writer: self.writer,
485                }
486                .into(),
487            )?;
488        }
489        Ok(())
490    }
491
492    fn newline(&mut self) -> Result<(), std::fmt::Error> {
493        self.writer.write_str("\n")
494    }
495    fn write_prefix_name_unit(&mut self) -> Result<(), std::fmt::Error> {
496        if let Some(prefix) = self.prefix {
497            self.writer.write_str(prefix.as_str())?;
498            self.writer.write_str("_")?;
499        }
500        self.writer.write_str(self.name)?;
501        if let Some(unit) = self.unit {
502            self.writer.write_str("_")?;
503            self.writer.write_str(unit.as_str())?;
504        }
505
506        Ok(())
507    }
508
509    fn write_suffix(&mut self, suffix: &'static str) -> Result<(), std::fmt::Error> {
510        self.writer.write_str("_")?;
511        self.writer.write_str(suffix)?;
512
513        Ok(())
514    }
515
516    // TODO: Consider caching the encoded labels for Histograms as they stay the
517    // same but are currently encoded multiple times.
518    fn encode_labels<S: EncodeLabelSet>(
519        &mut self,
520        additional_labels: Option<&S>,
521    ) -> Result<(), std::fmt::Error> {
522        if self.const_labels.is_empty()
523            && additional_labels.is_none()
524            && self.family_labels.is_none()
525        {
526            return Ok(());
527        }
528
529        self.writer.write_str("{")?;
530
531        self.const_labels
532            .encode(&mut LabelSetEncoder::new(self.writer).into())?;
533
534        if let Some(additional_labels) = additional_labels {
535            if !self.const_labels.is_empty() {
536                self.writer.write_str(",")?;
537            }
538
539            additional_labels.encode(&mut LabelSetEncoder::new(self.writer).into())?;
540        }
541
542        /// Writer impl which prepends a comma on the first call to write output to the wrapped writer
543        struct CommaPrependingWriter<'a> {
544            writer: &'a mut dyn Write,
545            should_prepend: bool,
546        }
547
548        impl Write for CommaPrependingWriter<'_> {
549            fn write_str(&mut self, s: &str) -> std::fmt::Result {
550                if self.should_prepend {
551                    self.writer.write_char(',')?;
552                    self.should_prepend = false;
553                }
554                self.writer.write_str(s)
555            }
556        }
557
558        if let Some(labels) = self.family_labels {
559            // if const labels or additional labels have been written, a comma must be prepended before writing the family labels.
560            // However, it could be the case that the family labels are `Some` and yet empty, so the comma should _only_
561            // be prepended if one of the `Write` methods are actually called when attempting to write the family labels.
562            // Therefore, wrap the writer on `Self` with a CommaPrependingWriter if other labels have been written and
563            // there may be a need to prepend an extra comma before writing additional labels.
564            if !self.const_labels.is_empty() || additional_labels.is_some() {
565                let mut writer = CommaPrependingWriter {
566                    writer: self.writer,
567                    should_prepend: true,
568                };
569                labels.encode(&mut LabelSetEncoder::new(&mut writer).into())?;
570            } else {
571                labels.encode(&mut LabelSetEncoder::new(self.writer).into())?;
572            };
573        }
574
575        self.writer.write_str("}")?;
576
577        Ok(())
578    }
579}
580
581pub(crate) struct CounterValueEncoder<'a> {
582    writer: &'a mut dyn Write,
583}
584
585impl std::fmt::Debug for CounterValueEncoder<'_> {
586    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
587        f.debug_struct("CounterValueEncoder").finish()
588    }
589}
590
591impl CounterValueEncoder<'_> {
592    pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
593        self.writer.write_str(" ")?;
594        self.writer.write_str(dtoa::Buffer::new().format(v))?;
595        Ok(())
596    }
597
598    pub fn encode_u64(&mut self, v: u64) -> Result<(), std::fmt::Error> {
599        self.writer.write_str(" ")?;
600        self.writer.write_str(itoa::Buffer::new().format(v))?;
601        Ok(())
602    }
603}
604
605pub(crate) struct GaugeValueEncoder<'a> {
606    writer: &'a mut dyn Write,
607}
608
609impl std::fmt::Debug for GaugeValueEncoder<'_> {
610    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611        f.debug_struct("GaugeValueEncoder").finish()
612    }
613}
614
615impl GaugeValueEncoder<'_> {
616    pub fn encode_u32(&mut self, v: u32) -> Result<(), std::fmt::Error> {
617        self.writer.write_str(" ")?;
618        self.writer.write_str(itoa::Buffer::new().format(v))?;
619        Ok(())
620    }
621
622    pub fn encode_u64(&mut self, v: u64) -> Result<(), std::fmt::Error> {
623        self.writer.write_str(" ")?;
624        self.writer.write_str(itoa::Buffer::new().format(v))?;
625        Ok(())
626    }
627
628    pub fn encode_i64(&mut self, v: i64) -> Result<(), std::fmt::Error> {
629        self.writer.write_str(" ")?;
630        self.writer.write_str(itoa::Buffer::new().format(v))?;
631        Ok(())
632    }
633
634    pub fn encode_f64(&mut self, v: f64) -> Result<(), std::fmt::Error> {
635        self.writer.write_str(" ")?;
636        self.writer.write_str(dtoa::Buffer::new().format(v))?;
637        Ok(())
638    }
639}
640
641pub(crate) struct ExemplarValueEncoder<'a> {
642    writer: &'a mut dyn Write,
643}
644
645impl std::fmt::Debug for ExemplarValueEncoder<'_> {
646    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
647        f.debug_struct("ExemplarValueEncoder").finish()
648    }
649}
650
651impl ExemplarValueEncoder<'_> {
652    pub fn encode(&mut self, v: f64) -> Result<(), std::fmt::Error> {
653        self.writer.write_str(dtoa::Buffer::new().format(v))
654    }
655}
656
657pub(crate) struct LabelSetEncoder<'a> {
658    writer: &'a mut dyn Write,
659    first: bool,
660}
661
662impl std::fmt::Debug for LabelSetEncoder<'_> {
663    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
664        f.debug_struct("LabelSetEncoder")
665            .field("first", &self.first)
666            .finish()
667    }
668}
669
670impl<'a> LabelSetEncoder<'a> {
671    fn new(writer: &'a mut dyn Write) -> Self {
672        Self {
673            writer,
674            first: true,
675        }
676    }
677
678    pub fn encode_label(&mut self) -> LabelEncoder<'_> {
679        let first = self.first;
680        self.first = false;
681        LabelEncoder {
682            writer: self.writer,
683            first,
684        }
685    }
686}
687
688pub(crate) struct LabelEncoder<'a> {
689    writer: &'a mut dyn Write,
690    first: bool,
691}
692
693impl std::fmt::Debug for LabelEncoder<'_> {
694    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
695        f.debug_struct("LabelEncoder")
696            .field("first", &self.first)
697            .finish()
698    }
699}
700
701impl LabelEncoder<'_> {
702    pub fn encode_label_key(&mut self) -> Result<LabelKeyEncoder<'_>, std::fmt::Error> {
703        if !self.first {
704            self.writer.write_str(",")?;
705        }
706        Ok(LabelKeyEncoder {
707            writer: self.writer,
708        })
709    }
710}
711
712pub(crate) struct LabelKeyEncoder<'a> {
713    writer: &'a mut dyn Write,
714}
715
716impl std::fmt::Debug for LabelKeyEncoder<'_> {
717    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718        f.debug_struct("LabelKeyEncoder").finish()
719    }
720}
721
722impl<'a> LabelKeyEncoder<'a> {
723    pub fn encode_label_value(self) -> Result<LabelValueEncoder<'a>, std::fmt::Error> {
724        self.writer.write_str("=\"")?;
725        Ok(LabelValueEncoder {
726            writer: self.writer,
727        })
728    }
729}
730
731impl std::fmt::Write for LabelKeyEncoder<'_> {
732    fn write_str(&mut self, s: &str) -> std::fmt::Result {
733        self.writer.write_str(s)
734    }
735}
736
737pub(crate) struct LabelValueEncoder<'a> {
738    writer: &'a mut dyn Write,
739}
740
741impl std::fmt::Debug for LabelValueEncoder<'_> {
742    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
743        f.debug_struct("LabelValueEncoder").finish()
744    }
745}
746
747impl LabelValueEncoder<'_> {
748    pub fn finish(self) -> Result<(), std::fmt::Error> {
749        self.writer.write_str("\"")
750    }
751}
752
753impl std::fmt::Write for LabelValueEncoder<'_> {
754    fn write_str(&mut self, s: &str) -> std::fmt::Result {
755        self.writer.write_str(s)
756    }
757}
758
759#[cfg(test)]
760mod tests {
761    use super::*;
762    use crate::metrics::exemplar::HistogramWithExemplars;
763    use crate::metrics::family::Family;
764    use crate::metrics::gauge::Gauge;
765    use crate::metrics::histogram::{exponential_buckets, Histogram, NativeHistogramConfig};
766    use crate::metrics::info::Info;
767    use crate::metrics::{counter::Counter, exemplar::CounterWithExemplar};
768    use pyo3::{prelude::*, types::PyModule};
769    use std::borrow::Cow;
770    use std::fmt::Error;
771    use std::sync::atomic::{AtomicI32, AtomicU32};
772    use std::time::{SystemTime, UNIX_EPOCH};
773
774    #[test]
775    fn encode_counter() {
776        let counter: Counter = Counter::default();
777        let mut registry = Registry::default();
778        registry.register("my_counter", "My counter", counter);
779
780        let counter_f32 = Counter::<f32, AtomicU32>::default();
781        registry.register("f32_counter", "Counter::<f32, AtomicU32>", counter_f32);
782        let counter_u32 = Counter::<u32, AtomicU32>::default();
783        registry.register("u32_counter", "Counter::<u32, AtomicU32>", counter_u32);
784        let mut encoded = String::new();
785
786        encode(&mut encoded, &registry).unwrap();
787
788        parse_with_python_client(encoded);
789    }
790
791    #[test]
792    fn encode_counter_with_unit() {
793        let mut registry = Registry::default();
794        let counter: Counter = Counter::default();
795        registry.register_with_unit("my_counter", "My counter", Unit::Seconds, counter);
796
797        let mut encoded = String::new();
798        encode(&mut encoded, &registry).unwrap();
799
800        let expected = "# HELP my_counter_seconds My counter.\n".to_owned()
801            + "# TYPE my_counter_seconds counter\n"
802            + "# UNIT my_counter_seconds seconds\n"
803            + "my_counter_seconds_total 0\n"
804            + "# EOF\n";
805        assert_eq!(expected, encoded);
806
807        parse_with_python_client(encoded);
808    }
809
810    #[test]
811    fn encode_counter_with_exemplar() {
812        let now = SystemTime::now();
813
814        let mut registry = Registry::default();
815
816        let counter_with_exemplar: CounterWithExemplar<Vec<(String, u64)>> =
817            CounterWithExemplar::default();
818        registry.register_with_unit(
819            "my_counter_with_exemplar",
820            "My counter with exemplar",
821            Unit::Seconds,
822            counter_with_exemplar.clone(),
823        );
824
825        counter_with_exemplar.inc_by(1, Some(vec![("user_id".to_string(), 42)]), None);
826
827        let mut encoded = String::new();
828        encode(&mut encoded, &registry).unwrap();
829
830        let expected = "# HELP my_counter_with_exemplar_seconds My counter with exemplar.\n"
831            .to_owned()
832            + "# TYPE my_counter_with_exemplar_seconds counter\n"
833            + "# UNIT my_counter_with_exemplar_seconds seconds\n"
834            + "my_counter_with_exemplar_seconds_total 1 # {user_id=\"42\"} 1.0\n"
835            + "# EOF\n";
836        assert_eq!(expected, encoded);
837
838        parse_with_python_client(encoded);
839
840        counter_with_exemplar.inc_by(1, Some(vec![("user_id".to_string(), 99)]), Some(now));
841
842        let mut encoded = String::new();
843        encode(&mut encoded, &registry).unwrap();
844
845        let expected = "# HELP my_counter_with_exemplar_seconds My counter with exemplar.\n"
846            .to_owned()
847            + "# TYPE my_counter_with_exemplar_seconds counter\n"
848            + "# UNIT my_counter_with_exemplar_seconds seconds\n"
849            + "my_counter_with_exemplar_seconds_total 2 # {user_id=\"99\"} 1.0 "
850            + dtoa::Buffer::new().format(now.duration_since(UNIX_EPOCH).unwrap().as_secs_f64())
851            + "\n"
852            + "# EOF\n";
853        assert_eq!(expected, encoded);
854
855        parse_with_python_client(encoded);
856    }
857
858    #[test]
859    fn encode_gauge() {
860        let mut registry = Registry::default();
861        let gauge: Gauge = Gauge::default();
862        registry.register("my_gauge", "My gauge", gauge);
863        let gauge = Gauge::<u32, AtomicU32>::default();
864        registry.register("u32_gauge", "Gauge::<u32, AtomicU32>", gauge);
865
866        let gauge_f32 = Gauge::<f32, AtomicU32>::default();
867        registry.register("f32_gauge", "Gauge::<f32, AtomicU32>", gauge_f32);
868
869        let gauge_i32 = Gauge::<i32, AtomicI32>::default();
870        registry.register("i32_gauge", "Gauge::<i32, AtomicU32>", gauge_i32);
871
872        let mut encoded = String::new();
873
874        encode(&mut encoded, &registry).unwrap();
875
876        parse_with_python_client(encoded);
877    }
878
879    #[test]
880    fn encode_counter_family() {
881        let mut registry = Registry::default();
882        let family = Family::<Vec<(String, String)>, Counter>::default();
883        registry.register("my_counter_family", "My counter family", family.clone());
884
885        family
886            .get_or_create(&vec![
887                ("method".to_string(), "GET".to_string()),
888                ("status".to_string(), "200".to_string()),
889            ])
890            .inc();
891
892        let mut encoded = String::new();
893
894        encode(&mut encoded, &registry).unwrap();
895
896        parse_with_python_client(encoded);
897    }
898
899    #[test]
900    fn encode_counter_family_with_prefix_with_label() {
901        let mut registry = Registry::default();
902        let sub_registry = registry.sub_registry_with_prefix("my_prefix");
903        let sub_sub_registry = sub_registry
904            .sub_registry_with_label((Cow::Borrowed("my_key"), Cow::Borrowed("my_value")));
905        let family = Family::<Vec<(String, String)>, Counter>::default();
906        sub_sub_registry.register("my_counter_family", "My counter family", family.clone());
907
908        family
909            .get_or_create(&vec![
910                ("method".to_string(), "GET".to_string()),
911                ("status".to_string(), "200".to_string()),
912            ])
913            .inc();
914
915        let mut encoded = String::new();
916
917        encode(&mut encoded, &registry).unwrap();
918
919        let expected = "# HELP my_prefix_my_counter_family My counter family.\n"
920            .to_owned()
921            + "# TYPE my_prefix_my_counter_family counter\n"
922            + "my_prefix_my_counter_family_total{my_key=\"my_value\",method=\"GET\",status=\"200\"} 1\n"
923            + "# EOF\n";
924        assert_eq!(expected, encoded);
925
926        parse_with_python_client(encoded);
927    }
928
929    #[test]
930    fn encode_info() {
931        let mut registry = Registry::default();
932        let info = Info::new(vec![("os".to_string(), "GNU/linux".to_string())]);
933        registry.register("my_info_metric", "My info metric", info);
934
935        let mut encoded = String::new();
936        encode(&mut encoded, &registry).unwrap();
937
938        let expected = "# HELP my_info_metric My info metric.\n".to_owned()
939            + "# TYPE my_info_metric info\n"
940            + "my_info_metric_info{os=\"GNU/linux\"} 1\n"
941            + "# EOF\n";
942        assert_eq!(expected, encoded);
943
944        parse_with_python_client(encoded);
945    }
946
947    #[test]
948    fn encode_histogram() {
949        let mut registry = Registry::default();
950        let histogram = Histogram::new(exponential_buckets(1.0, 2.0, 10));
951        registry.register("my_histogram", "My histogram", histogram.clone());
952        histogram.observe(1.0);
953
954        let mut encoded = String::new();
955
956        encode(&mut encoded, &registry).unwrap();
957
958        parse_with_python_client(encoded);
959    }
960
961    #[test]
962    fn encode_classic_and_native_histogram_as_classic_text() {
963        let mut registry = Registry::default();
964        let histogram = Histogram::new_classic_and_native(
965            [1.0, 2.0],
966            NativeHistogramConfig::with_bucket_factor(1.1),
967        );
968        registry.register("my_histogram", "My histogram", histogram.clone());
969        histogram.observe(1.0);
970
971        let mut encoded = String::new();
972        encode(&mut encoded, &registry).unwrap();
973
974        let expected = "# HELP my_histogram My histogram.\n".to_owned()
975            + "# TYPE my_histogram histogram\n"
976            + "my_histogram_sum 1.0\n"
977            + "my_histogram_count 1\n"
978            + "my_histogram_bucket{le=\"1.0\"} 1\n"
979            + "my_histogram_bucket{le=\"2.0\"} 1\n"
980            + "my_histogram_bucket{le=\"+Inf\"} 1\n"
981            + "# EOF\n";
982        assert_eq!(expected, encoded);
983    }
984
985    #[test]
986    fn encode_native_only_histogram_errors() {
987        let mut registry = Registry::default();
988        let histogram = Histogram::new_native(NativeHistogramConfig::with_schema(0));
989        registry.register("my_histogram", "My histogram", histogram.clone());
990        histogram.observe(1.0);
991
992        let mut encoded = String::new();
993        assert!(encode(&mut encoded, &registry).is_err());
994    }
995
996    #[test]
997    fn encode_histogram_family() {
998        let mut registry = Registry::default();
999        let family =
1000            Family::new_with_constructor(|| Histogram::new(exponential_buckets(1.0, 2.0, 10)));
1001        registry.register("my_histogram", "My histogram", family.clone());
1002        family
1003            .get_or_create(&vec![
1004                ("method".to_string(), "GET".to_string()),
1005                ("status".to_string(), "200".to_string()),
1006            ])
1007            .observe(1.0);
1008
1009        let mut encoded = String::new();
1010
1011        encode(&mut encoded, &registry).unwrap();
1012
1013        parse_with_python_client(encoded);
1014    }
1015
1016    #[test]
1017    fn encode_histogram_family_with_empty_struct_family_labels() {
1018        let mut registry = Registry::default();
1019        let family =
1020            Family::new_with_constructor(|| Histogram::new(exponential_buckets(1.0, 2.0, 10)));
1021        registry.register("my_histogram", "My histogram", family.clone());
1022
1023        #[derive(Eq, PartialEq, Hash, Debug, Clone)]
1024        struct EmptyLabels {}
1025
1026        impl EncodeLabelSet for EmptyLabels {
1027            fn encode(&self, _encoder: &mut crate::encoding::LabelSetEncoder) -> Result<(), Error> {
1028                Ok(())
1029            }
1030        }
1031
1032        family.get_or_create(&EmptyLabels {}).observe(1.0);
1033
1034        let mut encoded = String::new();
1035
1036        encode(&mut encoded, &registry).unwrap();
1037
1038        parse_with_python_client(encoded);
1039    }
1040
1041    #[test]
1042    fn encode_histogram_with_exemplars() {
1043        let now = SystemTime::now();
1044
1045        let mut registry = Registry::default();
1046        let histogram = HistogramWithExemplars::new(exponential_buckets(1.0, 2.0, 10));
1047        registry.register("my_histogram", "My histogram", histogram.clone());
1048
1049        histogram.observe(1.0, Some([("user_id".to_string(), 42u64)]), Some(now));
1050        histogram.observe(2.0, Some([("user_id".to_string(), 99u64)]), None);
1051
1052        let mut encoded = String::new();
1053        encode(&mut encoded, &registry).unwrap();
1054
1055        let expected = "# HELP my_histogram My histogram.\n".to_owned()
1056            + "# TYPE my_histogram histogram\n"
1057            + "my_histogram_sum 3.0\n"
1058            + "my_histogram_count 2\n"
1059            + "my_histogram_bucket{le=\"1.0\"} 1 # {user_id=\"42\"} 1.0 "
1060            + dtoa::Buffer::new().format(now.duration_since(UNIX_EPOCH).unwrap().as_secs_f64())
1061            + "\n"
1062            + "my_histogram_bucket{le=\"2.0\"} 2 # {user_id=\"99\"} 2.0\n"
1063            + "my_histogram_bucket{le=\"4.0\"} 2\n"
1064            + "my_histogram_bucket{le=\"8.0\"} 2\n"
1065            + "my_histogram_bucket{le=\"16.0\"} 2\n"
1066            + "my_histogram_bucket{le=\"32.0\"} 2\n"
1067            + "my_histogram_bucket{le=\"64.0\"} 2\n"
1068            + "my_histogram_bucket{le=\"128.0\"} 2\n"
1069            + "my_histogram_bucket{le=\"256.0\"} 2\n"
1070            + "my_histogram_bucket{le=\"512.0\"} 2\n"
1071            + "my_histogram_bucket{le=\"+Inf\"} 2\n"
1072            + "# EOF\n";
1073        assert_eq!(expected, encoded);
1074
1075        parse_with_python_client(encoded);
1076    }
1077
1078    #[test]
1079    fn sub_registry_with_prefix_and_label() {
1080        let top_level_metric_name = "my_top_level_metric";
1081        let mut registry = Registry::default();
1082        let counter: Counter = Counter::default();
1083        registry.register(top_level_metric_name, "some help", counter.clone());
1084
1085        let prefix_1 = "prefix_1";
1086        let prefix_1_metric_name = "my_prefix_1_metric";
1087        let sub_registry = registry.sub_registry_with_prefix(prefix_1);
1088        sub_registry.register(prefix_1_metric_name, "some help", counter.clone());
1089
1090        let prefix_1_1 = "prefix_1_1";
1091        let prefix_1_1_metric_name = "my_prefix_1_1_metric";
1092        let sub_sub_registry = sub_registry.sub_registry_with_prefix(prefix_1_1);
1093        sub_sub_registry.register(prefix_1_1_metric_name, "some help", counter.clone());
1094
1095        let label_1_2 = (Cow::Borrowed("registry"), Cow::Borrowed("1_2"));
1096        let prefix_1_2_metric_name = "my_prefix_1_2_metric";
1097        let sub_sub_registry = sub_registry.sub_registry_with_label(label_1_2.clone());
1098        sub_sub_registry.register(prefix_1_2_metric_name, "some help", counter.clone());
1099
1100        let labels_1_3 = vec![
1101            (Cow::Borrowed("label_1_3_1"), Cow::Borrowed("value_1_3_1")),
1102            (Cow::Borrowed("label_1_3_2"), Cow::Borrowed("value_1_3_2")),
1103        ];
1104        let prefix_1_3_metric_name = "my_prefix_1_3_metric";
1105        let sub_sub_registry =
1106            sub_registry.sub_registry_with_labels(labels_1_3.clone().into_iter());
1107        sub_sub_registry.register(prefix_1_3_metric_name, "some help", counter.clone());
1108
1109        let prefix_1_3_1 = "prefix_1_3_1";
1110        let prefix_1_3_1_metric_name = "my_prefix_1_3_1_metric";
1111        let sub_sub_sub_registry = sub_sub_registry.sub_registry_with_prefix(prefix_1_3_1);
1112        sub_sub_sub_registry.register(prefix_1_3_1_metric_name, "some help", counter.clone());
1113
1114        let prefix_2 = "prefix_2";
1115        let _ = registry.sub_registry_with_prefix(prefix_2);
1116
1117        let prefix_3 = "prefix_3";
1118        let prefix_3_metric_name = "my_prefix_3_metric";
1119        let sub_registry = registry.sub_registry_with_prefix(prefix_3);
1120        sub_registry.register(prefix_3_metric_name, "some help", counter);
1121
1122        let mut encoded = String::new();
1123        encode(&mut encoded, &registry).unwrap();
1124
1125        let expected = "# HELP my_top_level_metric some help.\n".to_owned()
1126            + "# TYPE my_top_level_metric counter\n"
1127            + "my_top_level_metric_total 0\n"
1128            + "# HELP prefix_1_my_prefix_1_metric some help.\n"
1129            + "# TYPE prefix_1_my_prefix_1_metric counter\n"
1130            + "prefix_1_my_prefix_1_metric_total 0\n"
1131            + "# HELP prefix_1_prefix_1_1_my_prefix_1_1_metric some help.\n"
1132            + "# TYPE prefix_1_prefix_1_1_my_prefix_1_1_metric counter\n"
1133            + "prefix_1_prefix_1_1_my_prefix_1_1_metric_total 0\n"
1134            + "# HELP prefix_1_my_prefix_1_2_metric some help.\n"
1135            + "# TYPE prefix_1_my_prefix_1_2_metric counter\n"
1136            + "prefix_1_my_prefix_1_2_metric_total{registry=\"1_2\"} 0\n"
1137            + "# HELP prefix_1_my_prefix_1_3_metric some help.\n"
1138            + "# TYPE prefix_1_my_prefix_1_3_metric counter\n"
1139            + "prefix_1_my_prefix_1_3_metric_total{label_1_3_1=\"value_1_3_1\",label_1_3_2=\"value_1_3_2\"} 0\n"
1140            + "# HELP prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric some help.\n"
1141            + "# TYPE prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric counter\n"
1142            + "prefix_1_prefix_1_3_1_my_prefix_1_3_1_metric_total{label_1_3_1=\"value_1_3_1\",label_1_3_2=\"value_1_3_2\"} 0\n"
1143            + "# HELP prefix_3_my_prefix_3_metric some help.\n"
1144            + "# TYPE prefix_3_my_prefix_3_metric counter\n"
1145            + "prefix_3_my_prefix_3_metric_total 0\n"
1146            + "# EOF\n";
1147        assert_eq!(expected, encoded);
1148
1149        parse_with_python_client(encoded);
1150    }
1151
1152    #[test]
1153    fn sub_registry_collector() {
1154        use crate::encoding::EncodeMetric;
1155
1156        #[derive(Debug)]
1157        struct Collector {
1158            name: String,
1159        }
1160
1161        impl Collector {
1162            fn new(name: impl Into<String>) -> Self {
1163                Self { name: name.into() }
1164            }
1165        }
1166
1167        impl crate::collector::Collector for Collector {
1168            fn encode(
1169                &self,
1170                mut encoder: crate::encoding::DescriptorEncoder,
1171            ) -> Result<(), std::fmt::Error> {
1172                let counter = crate::metrics::counter::ConstCounter::new(42u64);
1173                let metric_encoder = encoder.encode_descriptor(
1174                    &self.name,
1175                    "some help",
1176                    None,
1177                    counter.metric_type(),
1178                )?;
1179                counter.encode(metric_encoder)?;
1180                Ok(())
1181            }
1182        }
1183
1184        let mut registry = Registry::default();
1185        registry.register_collector(Box::new(Collector::new("top_level")));
1186
1187        let sub_registry = registry.sub_registry_with_prefix("prefix_1");
1188        sub_registry.register_collector(Box::new(Collector::new("sub_level")));
1189
1190        let sub_sub_registry = sub_registry.sub_registry_with_prefix("prefix_1_2");
1191        sub_sub_registry.register_collector(Box::new(Collector::new("sub_sub_level")));
1192
1193        let mut encoded = String::new();
1194        encode(&mut encoded, &registry).unwrap();
1195
1196        let expected = "# HELP top_level some help\n".to_owned()
1197            + "# TYPE top_level counter\n"
1198            + "top_level_total 42\n"
1199            + "# HELP prefix_1_sub_level some help\n"
1200            + "# TYPE prefix_1_sub_level counter\n"
1201            + "prefix_1_sub_level_total 42\n"
1202            + "# HELP prefix_1_prefix_1_2_sub_sub_level some help\n"
1203            + "# TYPE prefix_1_prefix_1_2_sub_sub_level counter\n"
1204            + "prefix_1_prefix_1_2_sub_sub_level_total 42\n"
1205            + "# EOF\n";
1206        assert_eq!(expected, encoded);
1207
1208        parse_with_python_client(encoded);
1209    }
1210
1211    #[test]
1212    fn label_sets_can_be_composed() {
1213        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
1214        struct Color(&'static str);
1215        impl EncodeLabelSet for Color {
1216            fn encode(
1217                &self,
1218                encoder: &mut crate::encoding::LabelSetEncoder,
1219            ) -> Result<(), std::fmt::Error> {
1220                use crate::encoding::EncodeLabel;
1221                let Self(color) = *self;
1222                let labels = ("color", color);
1223                let encoder = encoder.encode_label();
1224                labels.encode(encoder)
1225            }
1226        }
1227
1228        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
1229        struct Size(&'static str);
1230        impl EncodeLabelSet for Size {
1231            fn encode(
1232                &self,
1233                encoder: &mut crate::encoding::LabelSetEncoder,
1234            ) -> Result<(), std::fmt::Error> {
1235                use crate::encoding::EncodeLabel;
1236                let Self(size) = *self;
1237                let labels = ("size", size);
1238                let encoder = encoder.encode_label();
1239                labels.encode(encoder)
1240            }
1241        }
1242
1243        type Labels = (Color, Size);
1244
1245        let mut registry = Registry::default();
1246        let family = Family::<Labels, Counter>::default();
1247        registry.register("items", "Example metric", family.clone());
1248
1249        let labels = (Color("red"), Size("large"));
1250        let counter = family.get_or_create(&labels);
1251        counter.inc();
1252
1253        let mut encoded = String::new();
1254        encode(&mut encoded, &registry).unwrap();
1255
1256        let expected = "# HELP items Example metric.\n\
1257                        # TYPE items counter\n\
1258                        items_total{color=\"red\",size=\"large\"} 1\n\
1259                        # EOF\n";
1260        assert_eq!(expected, encoded);
1261
1262        parse_with_python_client(encoded);
1263    }
1264
1265    #[test]
1266    fn encode_registry_eof() {
1267        let mut orders_registry = Registry::default();
1268
1269        let total_orders: Counter<u64> = Default::default();
1270        orders_registry.register("orders", "Total orders received", total_orders.clone());
1271        total_orders.inc();
1272
1273        let processing_times = Histogram::new(exponential_buckets(1.0, 2.0, 10));
1274        orders_registry.register_with_unit(
1275            "processing_times",
1276            "Order times",
1277            Unit::Seconds,
1278            processing_times.clone(),
1279        );
1280        processing_times.observe(2.4);
1281
1282        let mut user_auth_registry = Registry::default();
1283
1284        let successful_logins: Counter<u64> = Default::default();
1285        user_auth_registry.register(
1286            "successful_logins",
1287            "Total successful logins",
1288            successful_logins.clone(),
1289        );
1290        successful_logins.inc();
1291
1292        let failed_logins: Counter<u64> = Default::default();
1293        user_auth_registry.register(
1294            "failed_logins",
1295            "Total failed logins",
1296            failed_logins.clone(),
1297        );
1298
1299        let mut response = String::new();
1300
1301        encode_registry(&mut response, &orders_registry).unwrap();
1302        assert_eq!(&response[response.len() - 20..], "bucket{le=\"+Inf\"} 1\n");
1303
1304        encode_registry(&mut response, &user_auth_registry).unwrap();
1305        assert_eq!(&response[response.len() - 20..], "iled_logins_total 0\n");
1306
1307        encode_eof(&mut response).unwrap();
1308        assert_eq!(&response[response.len() - 20..], "ogins_total 0\n# EOF\n");
1309    }
1310
1311    fn parse_with_python_client(input: String) {
1312        pyo3::Python::initialize();
1313
1314        println!("{input:?}");
1315        Python::attach(|py| {
1316            let parser = PyModule::from_code(
1317                py,
1318                c"
1319from prometheus_client.openmetrics.parser import text_string_to_metric_families
1320
1321def parse(input):
1322    families = text_string_to_metric_families(input)
1323    list(families)
1324",
1325                c"parser.py",
1326                c"parser",
1327            )
1328            .map_err(|e| e.to_string())
1329            .unwrap();
1330
1331            parser
1332                .getattr("parse")
1333                .expect("`parse` to exist.")
1334                .call1((input.clone(),))
1335                .map_err(|e| e.to_string())
1336                .unwrap();
1337        })
1338    }
1339
1340    #[test]
1341    fn encode_omit_empty() {
1342        let mut registry = Registry::default();
1343        let counter1: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1344        let counter2: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1345        let counter3: Family<Vec<(&'static str, &'static str)>, Counter> = Default::default();
1346
1347        registry.register("counter1", "First counter", counter1.clone());
1348        registry.register("counter2", "Second counter", counter2.clone());
1349        registry.register("counter3", "Third counter", counter3.clone());
1350
1351        counter1.get_or_create(&vec![("label", "value")]).inc();
1352
1353        let mut encoded = String::new();
1354        encode(&mut encoded, &registry).unwrap();
1355
1356        let expected = "# HELP counter1 First counter.\n".to_owned()
1357            + "# TYPE counter1 counter\n"
1358            + "counter1_total{label=\"value\"} 1\n"
1359            + "# EOF\n";
1360        assert_eq!(expected, encoded);
1361
1362        counter2.get_or_create(&vec![("label", "value")]).inc();
1363
1364        let mut encoded = String::new();
1365        encode(&mut encoded, &registry).unwrap();
1366
1367        let expected = "# HELP counter1 First counter.\n".to_owned()
1368            + "# TYPE counter1 counter\n"
1369            + "counter1_total{label=\"value\"} 1\n"
1370            + "# HELP counter2 Second counter.\n"
1371            + "# TYPE counter2 counter\n"
1372            + "counter2_total{label=\"value\"} 1\n"
1373            + "# EOF\n";
1374        assert_eq!(expected, encoded);
1375    }
1376}