diff --git a/crates/metrics/src/label.rs b/crates/metrics/src/label.rs index 84bf2a5..a8b8564 100644 --- a/crates/metrics/src/label.rs +++ b/crates/metrics/src/label.rs @@ -19,26 +19,6 @@ pub type Labeled2 = WithLabel>; pub type Labeled3 = WithLabel>>; pub type Labeled4 = WithLabel>>>; -/// Metric label defined as an `enum`. -/// -/// The most efficient way to specify metric labels in runtime, metric lookups -/// using enum labels are as fast as array indexing. Prefer using this when all -/// of your possible label values are known at the compile time. -/// -/// To implement this `trait` you also need to derive [`Ordinalize`] for your -/// `enum`. -/// SAFETY: DO NOT use custom discriminant values (eg. `enum MyEnum { MyVariant -/// = -1 }`), this will lead to either: -/// - `panic` in runtime (only for builds with `debug_assertions`) -/// - incorrect label resolution -pub trait EnumLabel: Copy + Ordinalize { - /// Name of the label. - const NAME: &'static str; - - /// String representation of the label value. - fn as_str(&self) -> &'static str; -} - pub trait DynamicLabel { type MetricCollection; } @@ -77,23 +57,58 @@ where } } -impl DynamicLabel for L +/// Metric label using an `enum` as its value. +/// +/// The most efficient way to specify metric labels in runtime, metric lookups +/// using enum labels are as fast as array indexing. Prefer using this when all +/// of your possible label values are known at the compile time. +#[derive(Clone, Copy, Debug)] +pub struct EnumLabel(T); + +impl EnumLabel { + /// Creates a new [`EnumLabel`]. + pub fn new(e: T) -> Self { + Self(e) + } + + /// Convert this [`EnumLabel`] into the inner [`Enum`]. + pub fn into_inner(self) -> T { + self.0 + } +} + +/// `enum` used as the value in [`EnumLabel`]s. +/// +/// To implement this `trait` you also need to derive [`Ordinalize`] for your +/// `enum`. +/// SAFETY: DO NOT use custom discriminant values (eg. `enum MyEnum { MyVariant +/// = -1 }`), this will lead to either: +/// - `panic` in runtime (only for builds with `debug_assertions`) +/// - incorrect label resolution +pub trait Enum: Copy + Ordinalize { + /// String representation of this enum. + fn as_str(&self) -> &'static str; +} + +impl DynamicLabel for EnumLabel where - L: EnumLabel, + T: Enum, { - // TODO: Switch to `[(L, M); L::VARIANT_COUNT]` once generic parameters are + // TODO: Switch to `[(T, M); T::VARIANT_COUNT]` once generic parameters are // allowed to be used in const expressions. - type MetricCollection = Vec<(L, M)>; + type MetricCollection = Vec<(T, M)>; } -impl Metric for WithLabel +impl Metric for WithLabel, M> where - L: EnumLabel, + T: Enum, M: Metric, { fn register(attrs: &Attrs) -> Self { - let metrics = L::VARIANTS.iter().map(|l| { - let label = Label::from_static_parts(L::NAME, l.as_str()); + let name = const { resolve_label_name::() }; + + let metrics = T::VARIANTS.iter().map(|l| { + let label = Label::from_static_parts(name, l.as_str()); (*l, M::register(&attrs.with_label(label))) }); @@ -103,21 +118,22 @@ where } } -impl ResolveLabels<(L,)> for WithLabel +impl ResolveLabels<(EnumLabel,)> + for WithLabel, M> where - L: EnumLabel, + T: Enum, M: Metric, { type Target = M; - fn resolve_labels(&self, labels: (L,)) -> &M { + fn resolve_labels(&self, (label,): (EnumLabel,)) -> &M { let debug_panic = || { if cfg!(debug_assertions) { panic!("Invalid enum usage, custom discriminants must not be used") } }; - let idx = labels.0.ordinal(); + let idx = label.0.ordinal(); let mut idx = if idx < 0 { debug_panic(); 0 diff --git a/crates/metrics/src/lib.rs b/crates/metrics/src/lib.rs index d75d5b9..6e6d4c0 100644 --- a/crates/metrics/src/lib.rs +++ b/crates/metrics/src/lib.rs @@ -19,6 +19,7 @@ //! enum_ordinalize::Ordinalize, //! label_name, //! BoolLabel, +//! Enum, //! EnumLabel, //! LabeledCounter2, //! LabeledGauge3, @@ -28,14 +29,14 @@ //! }; //! //! #[derive(Clone, Copy, Ordinalize)] -//! enum MyEnumLabel { +//! enum MyEnum { //! A, //! B, //! } //! -//! impl EnumLabel for MyEnumLabel { -//! const NAME: &'static str = "my_enum_label"; +//! type MyEnumLabel = EnumLabel<{ label_name("my_enum_label") }, MyEnum>; //! +//! impl Enum for MyEnum { //! fn as_str(&self) -> &'static str { //! match self { //! Self::A => "a", @@ -67,12 +68,12 @@ //! //! COUNTER_A.increment(1); //! GAUGE_A.set(42); -//! HISTOGRAM_A.record(1000, (MyEnumLabel::A,)); +//! HISTOGRAM_A.record(1000, (MyEnumLabel::new(MyEnum::A),)); //! COUNTER_B.increment(2u64, (MyStringLabel::new("test"), MyBoolLabel::new(false))); //! //! let labels = ( //! MyU8StringLabel::new(&42), -//! MyEnumLabel::B, +//! MyEnumLabel::new(MyEnum::B), //! MyBoolLabel::new(true), //! ); //! GAUGE_B.decrement(2, labels); @@ -81,7 +82,7 @@ pub use { backend, enum_ordinalize, - label::{label_name, BoolLabel, EnumLabel, LabelName, StringLabel, WithLabel}, + label::{label_name, BoolLabel, Enum, EnumLabel, LabelName, StringLabel, WithLabel}, lazy::Lazy, }; use {