Skip to content

[IDEA]: Add helper for implementing EncodeLabelSet manually #288

@nastynaz

Description

@nastynaz

Problem

Manually implementing EncodeLabelSet for a struct currently involves a lot of boilerplate:

pub struct MyLabels {
    pub method: &'static str,  // or String, Cow<'static, str>, etc.
    pub status: u16
   // many other fields
}

impl EncodeLabelSet for MyLabels {
    fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), fmt::Error> {
        // label: method = self.method
        {
            let mut label = encoder.encode_label();
            let mut key = label.encode_label_key()?;
            // field name -> key string (handle raw-idents manually if you used them)
            EncodeLabelKey::encode("method", &mut key)?;

            let mut value = key.encode_label_value()?;
            EncodeLabelValue::encode(&self.method, &mut value)?;
            value.finish()?;
        }
        // label: status = self.status
        {
            let mut label = encoder.encode_label();
            let mut key = label.encode_label_key()?;
            EncodeLabelKey::encode("status", &mut key)?;
            let mut value = key.encode_label_value()?;
            EncodeLabelValue::encode(&self.status, &mut value)?;
            value.finish()?;
        }

        // repeated for all other fields

        Ok(())
    }
}

This can become a chore if you have to implement it manually (e.g. because you want conditional encoding).

Solution

A new LabelSetWriter struct that allows you to easily specify (key, value) pairs to encode.

Usage

 pub struct MyLabels {
     pub status: u16,
     pub method: &'static str,
     pub optional: Option<&'static str>,
     // suppose we want to flatten these (like `#[prometheus(flatten)]`)
     pub common: CommonLabels
 }

 impl EncodeLabelSet for MyLabels {
     fn encode(&self, enc: &mut LabelSetEncoder) -> Result<(), fmt::Error> {
         let mut writer = LabelSetWriter::new(enc);
             .kv("status", &self.status)?
             .kv("method", &self.method)?;

         // allows for conditional encoding
         if let Some(optional) = &self.optional {
             writer.kv("optional", optional)?;
         }
        // allows flattening
         writer.flatten(&self.common)?;

         Ok(())
     }
 }

Discussion

I've written the code for LabelSetWriter and it's helpful for me in reducing boilerplate.
Would anyone else find this useful? I'm open to making a PR if so.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions