diff --git a/CHANGELOG.md b/CHANGELOG.md index c798c59..fb2c876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,13 +14,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Family::get_or_create_owned` can access a metric in a labeled family. This method avoids the risk of runtime deadlocks at the expense of creating an owned type. See [PR 244]. - + [PR 244]: https://github.com/prometheus/client_rust/pull/244 [PR 257]: https://github.com/prometheus/client_rust/pull/257 ### Changed - `EncodeLabelSet::encode()` now accepts a mutable reference to its encoder parameter. +- Emit better compilation error message when deriving marcos `EncodeLabelSet` and `EncodeLabelValue`. + See [PR 267]. + +### Fixed + +- Fixed an issue where the derive marcos `EncodeLabelSet` and `EncodeLabelValue` didn't work + when the `struct` has generic parameters (like `'a`). + See [PR 265]. + +- Fixed an issue where the derive macro `EncodeLabelSet` didn't work + when the `struct` has generic parameters (like `'a`). + See [PR 267]. + +[PR 265]: https://github.com/prometheus/client_rust/pull/265 +[PR 267]: https://github.com/prometheus/client_rust/pull/266 ## [0.23.1] diff --git a/derive-encode/src/lib.rs b/derive-encode/src/lib.rs index a3c0aac..e93f1b6 100644 --- a/derive-encode/src/lib.rs +++ b/derive-encode/src/lib.rs @@ -8,7 +8,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; -use quote::quote; +use quote::{quote, ToTokens}; use syn::DeriveInput; /// Derive `prometheus_client::encoding::EncodeLabelSet`. @@ -60,18 +60,41 @@ pub fn derive_encode_label_set(input: TokenStream) -> TokenStream { }) .collect(), syn::Fields::Unnamed(_) => { - panic!("Can not derive Encode for struct with unnamed fields.") + return syn::Error::new_spanned( + name, + "Can not derive `EncodeLabelSet` for struct with unnamed fields.", + ) + .to_compile_error() + .to_token_stream() + .into(); + } + syn::Fields::Unit => { + return syn::Error::new_spanned( + name, + "Can not derive `EncodeLabelSet` for unit struct.", + ) + .to_compile_error() + .to_token_stream() + .into(); } - 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.") + return syn::Error::new_spanned(name, "Can not derive `EncodeLabelSet` for enum.") + .to_compile_error() + .to_token_stream() + .into(); + } + syn::Data::Union(_) => { + return syn::Error::new_spanned(name, "Can not derive `EncodeLabelSet` for union.") + .to_compile_error() + .to_token_stream() + .into() } - syn::Data::Union(_) => panic!("Can not derive Encode for union."), }; + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let gen = quote! { - impl ::prometheus_client::encoding::EncodeLabelSet for #name { + impl #impl_generics ::prometheus_client::encoding::EncodeLabelSet for #name #ty_generics #where_clause { fn encode(&self, encoder: &mut ::prometheus_client::encoding::LabelSetEncoder) -> ::core::result::Result<(), ::core::fmt::Error> { use ::prometheus_client::encoding::EncodeLabel; use ::prometheus_client::encoding::EncodeLabelKey; @@ -95,7 +118,10 @@ pub fn derive_encode_label_value(input: TokenStream) -> TokenStream { let body = match ast.clone().data { syn::Data::Struct(_) => { - panic!("Can not derive EncodeLabel for struct.") + return syn::Error::new_spanned(name, "Can not derive `EncodeLabelValue` for struct.") + .to_compile_error() + .to_token_stream() + .into(); } syn::Data::Enum(syn::DataEnum { variants, .. }) => { let match_arms: TokenStream2 = variants @@ -114,7 +140,12 @@ pub fn derive_encode_label_value(input: TokenStream) -> TokenStream { } } } - syn::Data::Union(_) => panic!("Can not derive Encode for union."), + syn::Data::Union(_) => { + return syn::Error::new_spanned(name, "Can not derive `EncodeLabelValue` for union.") + .to_compile_error() + .to_token_stream() + .into() + } }; let gen = quote! { diff --git a/derive-encode/tests/build/friendly-compilation-error-msg.rs b/derive-encode/tests/build/friendly-compilation-error-msg.rs new file mode 100644 index 0000000..7931b30 --- /dev/null +++ b/derive-encode/tests/build/friendly-compilation-error-msg.rs @@ -0,0 +1,50 @@ +use prometheus_client::encoding::EncodeLabelSet; +use prometheus_client::encoding::EncodeLabelValue; + +mod A { + use super::*; + + #[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)] + struct Unnamed(String); + + #[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)] + struct Unit; + + #[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)] + enum Enum { + A, + B, + } + + #[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)] + enum DataEnum { + A, + B(String), + } + + #[derive(Clone, Copy, EncodeLabelSet)] + #[repr(C)] + union Union { + a: u32, + b: u64, + } +} + +mod B { + use super::*; + + #[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelValue)] + struct Struct { + a: String, + b: String, + } + + #[derive(Clone, Copy, EncodeLabelValue)] + #[repr(C)] + union Union { + a: u32, + b: u64, + } +} + +fn main() {} diff --git a/derive-encode/tests/build/friendly-compilation-error-msg.stderr b/derive-encode/tests/build/friendly-compilation-error-msg.stderr new file mode 100644 index 0000000..9486e7a --- /dev/null +++ b/derive-encode/tests/build/friendly-compilation-error-msg.stderr @@ -0,0 +1,41 @@ +error: Can not derive `EncodeLabelSet` for struct with unnamed fields. + --> tests/build/friendly-compilation-error-msg.rs:8:12 + | +8 | struct Unnamed(String); + | ^^^^^^^ + +error: Can not derive `EncodeLabelSet` for unit struct. + --> tests/build/friendly-compilation-error-msg.rs:11:12 + | +11 | struct Unit; + | ^^^^ + +error: Can not derive `EncodeLabelSet` for enum. + --> tests/build/friendly-compilation-error-msg.rs:14:10 + | +14 | enum Enum { + | ^^^^ + +error: Can not derive `EncodeLabelSet` for enum. + --> tests/build/friendly-compilation-error-msg.rs:20:10 + | +20 | enum DataEnum { + | ^^^^^^^^ + +error: Can not derive `EncodeLabelSet` for union. + --> tests/build/friendly-compilation-error-msg.rs:27:11 + | +27 | union Union { + | ^^^^^ + +error: Can not derive `EncodeLabelValue` for struct. + --> tests/build/friendly-compilation-error-msg.rs:37:12 + | +37 | struct Struct { + | ^^^^^^ + +error: Can not derive `EncodeLabelValue` for union. + --> tests/build/friendly-compilation-error-msg.rs:44:11 + | +44 | union Union { + | ^^^^^ diff --git a/derive-encode/tests/build/keep-impl-generics.rs b/derive-encode/tests/build/keep-impl-generics.rs new file mode 100644 index 0000000..208dee0 --- /dev/null +++ b/derive-encode/tests/build/keep-impl-generics.rs @@ -0,0 +1,18 @@ +use prometheus_client::encoding::EncodeLabelSet; +use prometheus_client::encoding::EncodeLabelValue; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelValue)] +enum CpuUsageLabelMode { + User, + System, + Irq, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)] +struct CpuUsageLabelSet<'a> { // <-- `'a` lifetime is used in the struct, + // this should be preserved in the impl block + mode: CpuUsageLabelMode, + service: &'a str +} + +fn main() {} diff --git a/derive-encode/tests/lib.rs b/derive-encode/tests/lib.rs index 5d0910f..555cc9c 100644 --- a/derive-encode/tests/lib.rs +++ b/derive-encode/tests/lib.rs @@ -209,5 +209,7 @@ fn flatten() { #[test] fn build() { let t = trybuild::TestCases::new(); - t.pass("tests/build/redefine-prelude-symbols.rs") + t.pass("tests/build/redefine-prelude-symbols.rs"); + t.pass("tests/build/keep-impl-generics.rs"); + t.compile_fail("tests/build/friendly-compilation-error-msg.rs"); }