prometheus-client-derive-encode-0.4.1/.cargo_vcs_info.json0000644000000001530000000000100172130ustar { "git": { "sha1": "9141c118d3c2194c841edca9219dab6d00a19a81" }, "path_in_vcs": "derive-encode" }prometheus-client-derive-encode-0.4.1/Cargo.toml0000644000000020260000000000100152120ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "prometheus-client-derive-encode" version = "0.4.1" authors = ["Max Inden "] description = "Auxiliary crate to derive Encode trait from prometheus-client." homepage = "https://github.com/prometheus/client_rust" documentation = "https://docs.rs/prometheus-client-derive-text-encode" license = "Apache-2.0 OR MIT" repository = "https://github.com/prometheus/client_rust" [lib] proc-macro = true [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "1" [dev-dependencies] prometheus-client-derive-encode-0.4.1/Cargo.toml.orig000064400000000000000000000012521046102023000206730ustar 00000000000000[package] name = "prometheus-client-derive-encode" version = "0.4.1" authors = ["Max Inden "] edition = "2021" description = "Auxiliary crate to derive Encode trait from prometheus-client." license = "Apache-2.0 OR MIT" repository = "https://github.com/prometheus/client_rust" homepage = "https://github.com/prometheus/client_rust" documentation = "https://docs.rs/prometheus-client-derive-text-encode" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] proc-macro2 = "1" quote = "1" syn = "1" [dev-dependencies] prometheus-client = { path = "../", features = ["protobuf"] } [lib] proc-macro = trueprometheus-client-derive-encode-0.4.1/src/lib.rs000064400000000000000000000141031046102023000177060ustar 00000000000000#![deny(dead_code)] #![deny(missing_docs)] #![deny(unused)] #![forbid(unsafe_code)] #![warn(missing_debug_implementations)] //! Derive crate for `prometheus_client`. use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::DeriveInput; /// Derive `prometheus_client::encoding::EncodeLabelSet`. #[proc_macro_derive(EncodeLabelSet, attributes(prometheus))] pub fn derive_encode_label_set(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); let name = &ast.ident; let body: TokenStream2 = match ast.clone().data { syn::Data::Struct(s) => match s.fields { syn::Fields::Named(syn::FieldsNamed { named, .. }) => named .into_iter() .map(|f| { let attribute = f .attrs .iter() .find(|a| a.path.is_ident("prometheus")) .map(|a| a.parse_args::().unwrap().to_string()); let flatten = match attribute.as_deref() { Some("flatten") => true, Some(other) => { panic!("Provided attribute '{other}', but only 'flatten' is supported") } None => false, }; let ident = f.ident.unwrap(); if flatten { quote! { EncodeLabelSet::encode(&self.#ident, encoder)?; } } else { let ident_string = KEYWORD_IDENTIFIERS .iter() .find(|pair| ident == pair.1) .map(|pair| pair.0.to_string()) .unwrap_or_else(|| ident.to_string()); quote! { let mut label_encoder = encoder.encode_label(); let mut label_key_encoder = label_encoder.encode_label_key()?; EncodeLabelKey::encode(&#ident_string, &mut label_key_encoder)?; let mut label_value_encoder = label_key_encoder.encode_label_value()?; EncodeLabelValue::encode(&self.#ident, &mut label_value_encoder)?; label_value_encoder.finish()?; } } }) .collect(), syn::Fields::Unnamed(_) => { panic!("Can not derive Encode for struct with unnamed fields.") } syn::Fields::Unit => panic!("Can not derive Encode for struct with unit field."), }, syn::Data::Enum(syn::DataEnum { .. }) => { panic!("Can not derive Encode for enum.") } syn::Data::Union(_) => panic!("Can not derive Encode for union."), }; let gen = quote! { impl prometheus_client::encoding::EncodeLabelSet for #name { fn encode(&self, mut encoder: prometheus_client::encoding::LabelSetEncoder) -> std::result::Result<(), std::fmt::Error> { use prometheus_client::encoding::EncodeLabel; use prometheus_client::encoding::EncodeLabelKey; use prometheus_client::encoding::EncodeLabelValue; #body Ok(()) } } }; gen.into() } /// Derive `prometheus_client::encoding::EncodeLabelValue`. #[proc_macro_derive(EncodeLabelValue)] pub fn derive_encode_label_value(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); let name = &ast.ident; let body = match ast.clone().data { syn::Data::Struct(_) => { panic!("Can not derive EncodeLabel for struct.") } syn::Data::Enum(syn::DataEnum { variants, .. }) => { let match_arms: TokenStream2 = variants .into_iter() .map(|v| { let ident = v.ident; quote! { #name::#ident => encoder.write_str(stringify!(#ident))?, } }) .collect(); quote! { match self { #match_arms } } } syn::Data::Union(_) => panic!("Can not derive Encode for union."), }; let gen = quote! { impl prometheus_client::encoding::EncodeLabelValue for #name { fn encode(&self, encoder: &mut prometheus_client::encoding::LabelValueEncoder) -> std::result::Result<(), std::fmt::Error> { use std::fmt::Write; #body Ok(()) } } }; gen.into() } // Copied from https://github.com/djc/askama (MIT and APACHE licensed) and // modified. static KEYWORD_IDENTIFIERS: [(&str, &str); 48] = [ ("as", "r#as"), ("break", "r#break"), ("const", "r#const"), ("continue", "r#continue"), ("crate", "r#crate"), ("else", "r#else"), ("enum", "r#enum"), ("extern", "r#extern"), ("false", "r#false"), ("fn", "r#fn"), ("for", "r#for"), ("if", "r#if"), ("impl", "r#impl"), ("in", "r#in"), ("let", "r#let"), ("loop", "r#loop"), ("match", "r#match"), ("mod", "r#mod"), ("move", "r#move"), ("mut", "r#mut"), ("pub", "r#pub"), ("ref", "r#ref"), ("return", "r#return"), ("static", "r#static"), ("struct", "r#struct"), ("trait", "r#trait"), ("true", "r#true"), ("type", "r#type"), ("unsafe", "r#unsafe"), ("use", "r#use"), ("where", "r#where"), ("while", "r#while"), ("async", "r#async"), ("await", "r#await"), ("dyn", "r#dyn"), ("abstract", "r#abstract"), ("become", "r#become"), ("box", "r#box"), ("do", "r#do"), ("final", "r#final"), ("macro", "r#macro"), ("override", "r#override"), ("priv", "r#priv"), ("typeof", "r#typeof"), ("unsized", "r#unsized"), ("virtual", "r#virtual"), ("yield", "r#yield"), ("try", "r#try"), ]; prometheus-client-derive-encode-0.4.1/tests/lib.rs000064400000000000000000000130631046102023000202650ustar 00000000000000use prometheus_client::encoding::text::encode; use prometheus_client::encoding::{EncodeLabelSet, EncodeLabelValue}; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; #[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug)] struct Labels { method: Method, path: String, } #[derive(Clone, Hash, PartialEq, Eq, EncodeLabelValue, Debug)] enum Method { Get, #[allow(dead_code)] Put, } #[test] fn basic_flow() { let mut registry = Registry::default(); let family = Family::::default(); registry.register("my_counter", "This is my counter", family.clone()); // Record a single HTTP GET request. family .get_or_create(&Labels { method: Method::Get, path: "/metrics".to_string(), }) .inc(); // Encode all metrics in the registry in the text format. let mut buffer = String::new(); encode(&mut buffer, ®istry).unwrap(); let expected = "# HELP my_counter This is my counter.\n".to_owned() + "# TYPE my_counter counter\n" + "my_counter_total{method=\"Get\",path=\"/metrics\"} 1\n" + "# EOF\n"; assert_eq!(expected, buffer); } mod protobuf { use crate::{Labels, Method}; use prometheus_client::encoding::protobuf::encode; use prometheus_client::encoding::protobuf::openmetrics_data_model; use prometheus_client::metrics::counter::Counter; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; #[test] fn structs() { let mut registry = Registry::default(); let family = Family::::default(); registry.register("my_counter", "This is my counter", family.clone()); // Record a single HTTP GET request. family .get_or_create(&Labels { method: Method::Get, path: "/metrics".to_string(), }) .inc(); // Encode all metrics in the registry in the OpenMetrics protobuf format. let mut metric_set = encode(®istry).unwrap(); let mut family: openmetrics_data_model::MetricFamily = metric_set.metric_families.pop().unwrap(); let metric: openmetrics_data_model::Metric = family.metrics.pop().unwrap(); let method = &metric.labels[0]; assert_eq!("method", method.name); assert_eq!("Get", method.value); let path = &metric.labels[1]; assert_eq!("path", path.name); assert_eq!("/metrics", path.value); } #[test] fn enums() { let mut registry = Registry::default(); let family = Family::::default(); registry.register("my_counter", "This is my counter", family.clone()); // Record a single HTTP GET request. family .get_or_create(&Labels { method: Method::Get, path: "/metrics".to_string(), }) .inc(); // Encode all metrics in the registry in the OpenMetrics protobuf format. let mut metric_set = encode(®istry).unwrap(); let mut family: openmetrics_data_model::MetricFamily = metric_set.metric_families.pop().unwrap(); let metric: openmetrics_data_model::Metric = family.metrics.pop().unwrap(); let label = &metric.labels[0]; assert_eq!("method", label.name); assert_eq!("Get", label.value); } } #[test] fn remap_keyword_identifiers() { #[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)] struct Labels { // `r#type` is problematic as `r#` is not a valid OpenMetrics label name // but one needs to use keyword identifier syntax (aka. raw identifiers) // as `type` is a keyword. // // Test makes sure `r#type` is replaced by `type` in the OpenMetrics // output. r#type: u64, } let mut registry = Registry::default(); let family = Family::::default(); registry.register("my_counter", "This is my counter", family.clone()); // Record a single HTTP GET request. family.get_or_create(&Labels { r#type: 42 }).inc(); // Encode all metrics in the registry in the text format. let mut buffer = String::new(); encode(&mut buffer, ®istry).unwrap(); let expected = "# HELP my_counter This is my counter.\n".to_owned() + "# TYPE my_counter counter\n" + "my_counter_total{type=\"42\"} 1\n" + "# EOF\n"; assert_eq!(expected, buffer); } #[test] fn flatten() { #[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)] struct CommonLabels { a: u64, b: u64, } #[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)] struct Labels { unique: u64, #[prometheus(flatten)] common: CommonLabels, } let mut registry = Registry::default(); let family = Family::::default(); registry.register("my_counter", "This is my counter", family.clone()); // Record a single HTTP GET request. family .get_or_create(&Labels { unique: 1, common: CommonLabels { a: 2, b: 3 }, }) .inc(); // Encode all metrics in the registry in the text format. let mut buffer = String::new(); encode(&mut buffer, ®istry).unwrap(); let expected = "# HELP my_counter This is my counter.\n".to_owned() + "# TYPE my_counter counter\n" + "my_counter_total{unique=\"1\",a=\"2\",b=\"3\"} 1\n" + "# EOF\n"; assert_eq!(expected, buffer); }