Skip to content

Commit 84e2cc6

Browse files
authored
feat(encoding)!: EncodeLabelSet::encode() uses reference (#257)
this commit alters the signature of the `EncodeLabelSet::encode()` trait method, such that it now accepts a mutable reference to its encoder. this is related to #135, and is a second proposal following previous work in #240. this change permits distinct label sets to be composed together, now that the label set encoder is not consumed. a new implementation for tuples `(A, B)` is provided. this commit includes a test case showing that a metric family can compose two label sets together, and that such a family can successfully be digested by the python client library. `derive-encode` is altered to generate code matching this new trait signature, and has been bumped to version 0.5.0 as a result of this breaking change in the `prometheus-client` library. Signed-off-by: katelyn martin <[email protected]>
1 parent 9a74e99 commit 84e2cc6

File tree

7 files changed

+103
-22
lines changed

7 files changed

+103
-22
lines changed

Diff for: CHANGELOG.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,17 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [0.23.1] - unreleased
7+
## [0.24.0] - unreleased
8+
9+
### Added
10+
11+
- `EncodeLabelSet` is now implemented for tuples `(A: EncodeLabelSet, B: EncodeLabelSet)`.
12+
13+
### Changed
14+
15+
- `EncodeLabelSet::encode()` now accepts a mutable reference to its encoder parameter.
16+
17+
## [0.23.1]
818

919
### Changed
1020

Diff for: Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "prometheus-client"
3-
version = "0.23.1"
3+
version = "0.24.0"
44
authors = ["Max Inden <[email protected]>"]
55
edition = "2021"
66
description = "Open Metrics client library allowing users to natively instrument applications."
@@ -21,7 +21,7 @@ members = ["derive-encode"]
2121
dtoa = "1.0"
2222
itoa = "1.0"
2323
parking_lot = "0.12"
24-
prometheus-client-derive-encode = { version = "0.4.1", path = "derive-encode" }
24+
prometheus-client-derive-encode = { version = "0.5.0", path = "derive-encode" }
2525
prost = { version = "0.12.0", optional = true }
2626
prost-types = { version = "0.12.0", optional = true }
2727

Diff for: derive-encode/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "prometheus-client-derive-encode"
3-
version = "0.4.2"
3+
version = "0.5.0"
44
authors = ["Max Inden <[email protected]>"]
55
edition = "2021"
66
description = "Auxiliary crate to derive Encode trait from prometheus-client."

Diff for: derive-encode/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub fn derive_encode_label_set(input: TokenStream) -> TokenStream {
7272

7373
let gen = quote! {
7474
impl prometheus_client::encoding::EncodeLabelSet for #name {
75-
fn encode(&self, mut encoder: prometheus_client::encoding::LabelSetEncoder) -> std::result::Result<(), std::fmt::Error> {
75+
fn encode(&self, encoder: &mut prometheus_client::encoding::LabelSetEncoder) -> std::result::Result<(), std::fmt::Error> {
7676
use prometheus_client::encoding::EncodeLabel;
7777
use prometheus_client::encoding::EncodeLabelKey;
7878
use prometheus_client::encoding::EncodeLabelValue;

Diff for: src/encoding.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl MetricEncoder<'_> {
203203
/// An encodable label set.
204204
pub trait EncodeLabelSet {
205205
/// Encode oneself into the given encoder.
206-
fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error>;
206+
fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), std::fmt::Error>;
207207
}
208208

209209
/// Encoder for a label set.
@@ -238,37 +238,53 @@ impl LabelSetEncoder<'_> {
238238
}
239239

240240
impl<T: EncodeLabel, const N: usize> EncodeLabelSet for [T; N] {
241-
fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> {
241+
fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), std::fmt::Error> {
242242
self.as_ref().encode(encoder)
243243
}
244244
}
245245

246246
impl<T: EncodeLabel> EncodeLabelSet for &[T] {
247-
fn encode(&self, mut encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> {
247+
fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), std::fmt::Error> {
248248
if self.is_empty() {
249249
return Ok(());
250250
}
251251

252252
for label in self.iter() {
253-
label.encode(encoder.encode_label())?
253+
let encoder = encoder.encode_label();
254+
label.encode(encoder)?
254255
}
255256

256257
Ok(())
257258
}
258259
}
259260

260261
impl<T: EncodeLabel> EncodeLabelSet for Vec<T> {
261-
fn encode(&self, encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> {
262+
fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), std::fmt::Error> {
262263
self.as_slice().encode(encoder)
263264
}
264265
}
265266

267+
impl<A, B> EncodeLabelSet for (A, B)
268+
where
269+
A: EncodeLabelSet,
270+
B: EncodeLabelSet,
271+
{
272+
fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), std::fmt::Error> {
273+
let (a, b) = self;
274+
275+
a.encode(encoder)?;
276+
b.encode(encoder)?;
277+
278+
Ok(())
279+
}
280+
}
281+
266282
/// Uninhabited type to represent the lack of a label set for a metric
267283
#[derive(Debug)]
268284
pub enum NoLabelSet {}
269285

270286
impl EncodeLabelSet for NoLabelSet {
271-
fn encode(&self, _encoder: LabelSetEncoder) -> Result<(), std::fmt::Error> {
287+
fn encode(&self, _encoder: &mut LabelSetEncoder) -> Result<(), std::fmt::Error> {
272288
Ok(())
273289
}
274290
}

Diff for: src/encoding/protobuf.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl DescriptorEncoder<'_> {
120120
};
121121
let mut labels = vec![];
122122
self.labels.encode(
123-
LabelSetEncoder {
123+
&mut LabelSetEncoder {
124124
labels: &mut labels,
125125
}
126126
.into(),
@@ -210,7 +210,7 @@ impl MetricEncoder<'_> {
210210
) -> Result<(), std::fmt::Error> {
211211
let mut info_labels = vec![];
212212
label_set.encode(
213-
LabelSetEncoder {
213+
&mut LabelSetEncoder {
214214
labels: &mut info_labels,
215215
}
216216
.into(),
@@ -235,7 +235,7 @@ impl MetricEncoder<'_> {
235235
) -> Result<MetricEncoder, std::fmt::Error> {
236236
let mut labels = self.labels.clone();
237237
label_set.encode(
238-
LabelSetEncoder {
238+
&mut LabelSetEncoder {
239239
labels: &mut labels,
240240
}
241241
.into(),
@@ -303,7 +303,7 @@ impl<S: EncodeLabelSet, V: EncodeExemplarValue> TryFrom<&Exemplar<S, V>>
303303

304304
let mut labels = vec![];
305305
exemplar.label_set.encode(
306-
LabelSetEncoder {
306+
&mut LabelSetEncoder {
307307
labels: &mut labels,
308308
}
309309
.into(),

Diff for: src/encoding/text.rs

+62-7
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,9 @@ pub(crate) struct MetricEncoder<'a> {
296296
impl std::fmt::Debug for MetricEncoder<'_> {
297297
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298298
let mut labels = String::new();
299+
let mut encoder = LabelSetEncoder::new(&mut labels).into();
299300
if let Some(l) = self.family_labels {
300-
l.encode(LabelSetEncoder::new(&mut labels).into())?;
301+
l.encode(&mut encoder)?;
301302
}
302303

303304
f.debug_struct("Encoder")
@@ -451,7 +452,7 @@ impl MetricEncoder<'_> {
451452
self.writer.write_str(" # {")?;
452453
exemplar
453454
.label_set
454-
.encode(LabelSetEncoder::new(self.writer).into())?;
455+
.encode(&mut LabelSetEncoder::new(self.writer).into())?;
455456
self.writer.write_str("} ")?;
456457
exemplar.value.encode(
457458
ExemplarValueEncoder {
@@ -502,14 +503,14 @@ impl MetricEncoder<'_> {
502503
self.writer.write_str("{")?;
503504

504505
self.const_labels
505-
.encode(LabelSetEncoder::new(self.writer).into())?;
506+
.encode(&mut LabelSetEncoder::new(self.writer).into())?;
506507

507508
if let Some(additional_labels) = additional_labels {
508509
if !self.const_labels.is_empty() {
509510
self.writer.write_str(",")?;
510511
}
511512

512-
additional_labels.encode(LabelSetEncoder::new(self.writer).into())?;
513+
additional_labels.encode(&mut LabelSetEncoder::new(self.writer).into())?;
513514
}
514515

515516
/// Writer impl which prepends a comma on the first call to write output to the wrapped writer
@@ -539,9 +540,9 @@ impl MetricEncoder<'_> {
539540
writer: self.writer,
540541
should_prepend: true,
541542
};
542-
labels.encode(LabelSetEncoder::new(&mut writer).into())?;
543+
labels.encode(&mut LabelSetEncoder::new(&mut writer).into())?;
543544
} else {
544-
labels.encode(LabelSetEncoder::new(self.writer).into())?;
545+
labels.encode(&mut LabelSetEncoder::new(self.writer).into())?;
545546
};
546547
}
547548

@@ -936,7 +937,7 @@ mod tests {
936937
struct EmptyLabels {}
937938

938939
impl EncodeLabelSet for EmptyLabels {
939-
fn encode(&self, _encoder: crate::encoding::LabelSetEncoder) -> Result<(), Error> {
940+
fn encode(&self, _encoder: &mut crate::encoding::LabelSetEncoder) -> Result<(), Error> {
940941
Ok(())
941942
}
942943
}
@@ -1114,6 +1115,60 @@ mod tests {
11141115
parse_with_python_client(encoded);
11151116
}
11161117

1118+
#[test]
1119+
fn label_sets_can_be_composed() {
1120+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1121+
struct Color(&'static str);
1122+
impl EncodeLabelSet for Color {
1123+
fn encode(
1124+
&self,
1125+
encoder: &mut crate::encoding::LabelSetEncoder,
1126+
) -> Result<(), std::fmt::Error> {
1127+
use crate::encoding::EncodeLabel;
1128+
let Self(color) = *self;
1129+
let labels = ("color", color);
1130+
let encoder = encoder.encode_label();
1131+
labels.encode(encoder)
1132+
}
1133+
}
1134+
1135+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1136+
struct Size(&'static str);
1137+
impl EncodeLabelSet for Size {
1138+
fn encode(
1139+
&self,
1140+
encoder: &mut crate::encoding::LabelSetEncoder,
1141+
) -> Result<(), std::fmt::Error> {
1142+
use crate::encoding::EncodeLabel;
1143+
let Self(size) = *self;
1144+
let labels = ("size", size);
1145+
let encoder = encoder.encode_label();
1146+
labels.encode(encoder)
1147+
}
1148+
}
1149+
1150+
type Labels = (Color, Size);
1151+
1152+
let mut registry = Registry::default();
1153+
let family = Family::<Labels, Counter>::default();
1154+
registry.register("items", "Example metric", family.clone());
1155+
1156+
let labels = (Color("red"), Size("large"));
1157+
let counter = family.get_or_create(&labels);
1158+
counter.inc();
1159+
1160+
let mut encoded = String::new();
1161+
encode(&mut encoded, &registry).unwrap();
1162+
1163+
let expected = "# HELP items Example metric.\n\
1164+
# TYPE items counter\n\
1165+
items_total{color=\"red\",size=\"large\"} 1\n\
1166+
# EOF\n";
1167+
assert_eq!(expected, encoded);
1168+
1169+
parse_with_python_client(encoded);
1170+
}
1171+
11171172
#[test]
11181173
fn encode_registry_eof() {
11191174
let mut orders_registry = Registry::default();

0 commit comments

Comments
 (0)