diff --git a/Cargo.lock b/Cargo.lock index 5547163..e066436 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,13 +36,13 @@ name = "alloc" version = "0.1.0" dependencies = [ "dhat", - "metrics 0.1.0", "serde", "serde_json", "thiserror", "tikv-jemalloc-ctl", "tikv-jemallocator", "tokio", + "wc_metrics", ] [[package]] @@ -1632,18 +1632,6 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" -[[package]] -name = "metrics" -version = "0.1.0" -dependencies = [ - "arc-swap", - "enum-ordinalize", - "metrics 0.23.0", - "parking_lot", - "pin-project", - "smallvec", -] - [[package]] name = "metrics" version = "0.23.0" @@ -1662,7 +1650,7 @@ checksum = "bf0af7a0d7ced10c0151f870e5e3f3f8bc9ffc5992d32873566ca1f9169ae776" dependencies = [ "base64 0.22.1", "indexmap", - "metrics 0.23.0", + "metrics", "metrics-util", "quanta", "thiserror", @@ -1677,7 +1665,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "hashbrown", - "metrics 0.23.0", + "metrics", "num_cpus", "quanta", "sketches-ddsketch", @@ -2968,12 +2956,24 @@ dependencies = [ "future", "geoip", "hyper 1.3.1", - "metrics 0.1.0", "metrics-exporter-prometheus", "rate_limit", "structopt", "tokio", "tower", + "wc_metrics", +] + +[[package]] +name = "wc_metrics" +version = "0.1.0" +dependencies = [ + "arc-swap", + "enum-ordinalize", + "metrics", + "parking_lot", + "pin-project", + "smallvec", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cab841a..34d7801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ analytics = { path = "./crates/analytics", optional = true } collections = { path = "./crates/collections", optional = true } future = { path = "./crates/future", optional = true } geoip = { path = "./crates/geoip", optional = true } -metrics = { path = "./crates/metrics", optional = true } +metrics = { package = "wc_metrics", path = "./crates/metrics", optional = true } rate_limit = { path = "./crates/rate_limit", optional = true } [dev-dependencies] diff --git a/crates/alloc/Cargo.toml b/crates/alloc/Cargo.toml index 6e8dda5..bc46b7c 100644 --- a/crates/alloc/Cargo.toml +++ b/crates/alloc/Cargo.toml @@ -10,7 +10,7 @@ profiler = ["dep:dhat", "dep:tokio"] metrics = ["dep:metrics"] [dependencies] -metrics = { path = "../metrics", optional = true } +metrics = { package = "wc_metrics", path = "../metrics", optional = true } tikv-jemallocator = { version = "0.5", features = ["stats"] } tikv-jemalloc-ctl = { version = "0.5", features = ["use_std"] } serde = { version = "1", features = ["derive"] } diff --git a/crates/metrics/Cargo.toml b/crates/metrics/Cargo.toml index 118c9c4..e9500fc 100644 --- a/crates/metrics/Cargo.toml +++ b/crates/metrics/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "metrics" +name = "wc_metrics" version = "0.1.0" edition = "2021" @@ -8,7 +8,7 @@ default = ["future"] future = ["dep:pin-project"] [dependencies] -backend = { package = "metrics", version = "0.23" } +metrics = "0.23" smallvec = "1" parking_lot = "0.12" enum-ordinalize = "4.3" diff --git a/crates/metrics/src/future.rs b/crates/metrics/src/future.rs index 6abae7e..12fd092 100644 --- a/crates/metrics/src/future.rs +++ b/crates/metrics/src/future.rs @@ -3,7 +3,15 @@ //! Usage: //! //! ``` -//! use metrics::{label_name, BoolLabel, FutureExt, FutureMetrics, LabeledFutureMetrics2, Lazy}; +//! use wc_metrics::{ +//! self as metrics, +//! label_name, +//! BoolLabel, +//! FutureExt, +//! FutureMetrics, +//! LabeledFutureMetrics2, +//! Lazy, +//! }; //! //! type MyBoolLabelA = BoolLabel<{ label_name("my_bool_label_a") }>; //! type MyBoolLabelB = BoolLabel<{ label_name("my_bool_label_b") }>; @@ -25,7 +33,7 @@ use { sealed::{Attrs, Metric}, Lazy, }, - backend::{counter, gauge, histogram, Counter, Gauge, Histogram, Label}, + metrics::{counter, gauge, histogram, Counter, Gauge, Histogram, Label}, std::{ future::Future, pin::Pin, diff --git a/crates/metrics/src/label.rs b/crates/metrics/src/label.rs index 5e3d9e8..b9d0416 100644 --- a/crates/metrics/src/label.rs +++ b/crates/metrics/src/label.rs @@ -4,8 +4,8 @@ use { Metric, }, arc_swap::ArcSwap, - backend::Label, enum_ordinalize::Ordinalize, + metrics::Label, parking_lot::Mutex, smallvec::SmallVec, std::{borrow::Borrow, collections::HashMap, marker::PhantomData, sync::Arc}, @@ -157,7 +157,7 @@ where /// name should be specified using the following hack: /// /// ``` -/// use metrics::{label_name, BoolLabel}; +/// use wc_metrics::{label_name, BoolLabel}; /// /// type MyLabel = BoolLabel<{ label_name("my_label") }>; /// ``` @@ -229,7 +229,7 @@ where /// name should be specified using the following hack: /// /// ``` -/// use metrics::{label_name, StringLabel}; +/// use wc_metrics::{label_name, StringLabel}; /// /// type MyLabel = StringLabel<{ label_name("my_label") }>; /// ``` diff --git a/crates/metrics/src/lazy.rs b/crates/metrics/src/lazy.rs index 7831fcf..04b264a 100644 --- a/crates/metrics/src/lazy.rs +++ b/crates/metrics/src/lazy.rs @@ -6,7 +6,7 @@ use { Metric, StaticAttrs, }, - backend::{Counter, Gauge, Histogram, IntoF64}, + metrics::{Counter, Gauge, Histogram, IntoF64}, std::sync::OnceLock, }; diff --git a/crates/metrics/src/lib.rs b/crates/metrics/src/lib.rs index c09f23b..8efd4b0 100644 --- a/crates/metrics/src/lib.rs +++ b/crates/metrics/src/lib.rs @@ -6,21 +6,27 @@ //! hurting the code ergonomics. //! //! A trivial atomic counter increment MUST NOT allocate stuff on the heap -//! (looking at you [`metrics::counter`]) and it SHOULD NOT acquire locks or -//! do [`HashMap`](std::collections::HashMap) lookups unless absolutely -//! necessary. +//! (looking at you [`metrics::counter`] and it SHOULD NOT acquire locks or do +//! [`HashMap`](std::collections::HashMap) lookups unless absolutely necessary. //! +//! If your metric is only being used once, or you can cache it somewhere +//! consider using [`counter`], [`gauge`] or [`histogram`] convinience macros. +//! The macros are completely optional and the machinery can be used +//! as is without them. //! //! # Usage //! //! ``` -//! use metrics::{ -//! backend::{Counter, Gauge, Histogram}, +//! use wc_metrics::{ +//! self as metrics, //! enum_ordinalize::Ordinalize, //! label_name, //! BoolLabel, +//! Counter, //! Enum, //! EnumLabel, +//! Gauge, +//! Histogram, //! LabeledCounter2, //! LabeledGauge3, //! LabeledHistogram, @@ -77,19 +83,20 @@ //! ``` pub use { - backend, enum_ordinalize, label::{label_name, BoolLabel, Enum, EnumLabel, LabelName, Optional, StringLabel, WithLabel}, lazy::Lazy, + metrics::{self as backend, Counter, Gauge, Histogram}, }; use { - backend::{Counter, Gauge, Histogram, IntoF64, Label}, label::{DynamicLabels, Labeled, Labeled2, Labeled3, Labeled4, StaticLabels}, + metrics::{IntoF64, Label}, sealed::{Attrs, Decrement, Execute, Increment, Metric, Record, Set}, }; mod label; mod lazy; +mod macros; #[cfg(feature = "future")] pub mod future; diff --git a/crates/metrics/src/macros.rs b/crates/metrics/src/macros.rs new file mode 100644 index 0000000..36d99be --- /dev/null +++ b/crates/metrics/src/macros.rs @@ -0,0 +1,500 @@ +/// Similar to [`metrics::counter`](crate::backend::counter), but expects +/// dynamic labels to be type-annotated and provides an option to specify +/// metric descriptions. +/// +/// Uses the machinery of this crate to create appropriately-typed `static` +/// metric and to resolve dynamic labels. +/// +/// Using this macro with the same arguments multilpe times is not recommended +/// as each time it creates a separate `static` variable. +/// If your metric needs to be modified from multiple places either store it +/// inside your own types, or use the vanilla machinery of this crate to define +/// your own `static` metric and use it instead. +/// +/// Usage: +/// ``` +/// use wc_metrics::{enum_ordinalize::Ordinalize, BoolLabel, EnumLabel, StringLabel, counter}; +/// +/// #[derive(Clone, Copy, Debug, Ordinalize)] +/// enum MyEnum { +/// A, +/// B, +/// } +/// +/// impl wc_metrics::Enum for MyEnum { +/// fn as_str(&self) -> &'static str { +/// match self { +/// Self::A => "a", +/// Self::B => "b", +/// } +/// } +/// } +/// +/// let s = "a"; +/// let b = true; +/// let u = 42; +/// let e = MyEnum::A; +/// +/// counter!("name").increment(1); +/// +/// counter!("name", EnumLabel<"a", MyEnum> => e).increment(1); +/// +/// counter!("name", BoolLabel<"a"> => b).increment(1); +/// +/// counter!("name", StringLabel<"a"> => s).increment(1); +/// +/// counter!("name", StringLabel<"a", u8> => &u).increment(1); +/// +/// counter!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b +/// ) +/// .increment(1); +/// +/// counter!("name", "a" => "1").increment(1); +/// +/// counter!("name", "a" => "1", "b" => "2").increment(1); +/// +/// counter!("name", StringLabel<"a", u8> => &u, "b" => "2").increment(1); +/// +/// counter!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b, +/// "e" => "1", +/// "f" => "2" +/// ) +/// .increment(1); +/// +/// counter!("name", "description").increment(1); +// +/// counter!("name", "description", EnumLabel<"a", MyEnum> => e).increment(1); +/// +/// counter!("name", "description", BoolLabel<"a"> => b).increment(1); +/// +/// counter!("name", "description", StringLabel<"a"> => s).increment(1); +/// +/// counter!("name", "description", StringLabel<"a", u8> => &u).increment(1); +/// +/// counter!("name", "description", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b +/// ) +/// .increment(1); +/// +/// counter!("name", "description", "a" => "1").increment(1); +/// +/// counter!("name", "description", "a" => "1", "b" => "2").increment(1); +/// +/// counter!("name", "description", StringLabel<"a", u8> => &u, "b" => +/// "2").increment(1); +/// +/// counter!("name", "description", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b, +/// "e" => "1", +/// "f" => "2" +/// ) +/// .increment(1); +/// ``` +#[macro_export] +macro_rules! counter { + ($($tail:tt)*) => { + $crate::metric!($crate::backend::Counter, $($tail)*) + }; +} + +/// Similar to [`metrics::gauge`](crate::backend::gauge), but expects +/// dynamic labels to be type-annotated and provides an option to specify +/// metric descriptions. +/// +/// Uses the machinery of this crate to create appropriately-typed `static` +/// metric and to resolve dynamic labels. +/// +/// Using this macro with the same arguments multilpe times is not recommended +/// as each time it creates a separate `static` variable. +/// If your metric needs to be modified from multiple places either store it +/// inside your own types, or use the vanilla machinery of this crate to define +/// your own `static` metric and use it instead. +/// +/// Usage: +/// ``` +/// use wc_metrics::{enum_ordinalize::Ordinalize, BoolLabel, EnumLabel, StringLabel, gauge}; +/// +/// #[derive(Clone, Copy, Debug, Ordinalize)] +/// enum MyEnum { +/// A, +/// B, +/// } +/// +/// impl wc_metrics::Enum for MyEnum { +/// fn as_str(&self) -> &'static str { +/// match self { +/// Self::A => "a", +/// Self::B => "b", +/// } +/// } +/// } +/// +/// let s = "a"; +/// let b = true; +/// let u = 42; +/// let e = MyEnum::A; +/// +/// gauge!("name").set(1); +/// +/// gauge!("name", EnumLabel<"a", MyEnum> => e).set(1); +/// +/// gauge!("name", BoolLabel<"a"> => b).set(1); +/// +/// gauge!("name", StringLabel<"a"> => s).set(1); +/// +/// gauge!("name", StringLabel<"a", u8> => &u).set(1); +/// +/// gauge!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b +/// ) +/// .set(1); +/// +/// gauge!("name", "a" => "1").set(1); +/// +/// gauge!("name", "a" => "1", "b" => "2").set(1); +/// +/// gauge!("name", StringLabel<"a", u8> => &u, "b" => "2").set(1); +/// +/// gauge!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b, +/// "e" => "1", +/// "f" => "2" +/// ) +/// .set(1); +/// +/// gauge!("name", "description").set(1); +// +/// gauge!("name", "description", EnumLabel<"a", MyEnum> => e).set(1); +/// +/// gauge!("name", "description", BoolLabel<"a"> => b).set(1); +/// +/// gauge!("name", "description", StringLabel<"a"> => s).set(1); +/// +/// gauge!("name", "description", StringLabel<"a", u8> => &u).set(1); +/// +/// gauge!("name", "description", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b +/// ) +/// .set(1); +/// +/// gauge!("name", "description", "a" => "1").set(1); +/// +/// gauge!("name", "description", "a" => "1", "b" => "2").set(1); +/// +/// gauge!("name", "description", StringLabel<"a", u8> => &u, "b" => +/// "2").set(1); +/// +/// gauge!("name", "description", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b, +/// "e" => "1", +/// "f" => "2" +/// ) +/// .set(1); +/// ``` +#[macro_export] +macro_rules! gauge { + ($($tail:tt)*) => { + $crate::metric!($crate::backend::Gauge, $($tail)*) + }; +} + +/// Similar to [`metrics::histogram`](crate::backend::histogram), but expects +/// dynamic labels to be type-annotated and provides an option to specify +/// metric descriptions. +/// +/// Uses the machinery of this crate to create appropriately-typed `static` +/// metric and to resolve dynamic labels. +/// +/// Using this macro with the same arguments multilpe times is not recommended +/// as each time it creates a separate `static` variable. +/// If your metric needs to be modified from multiple places either store it +/// inside your own types, or use the vanilla machinery of this crate to define +/// your own `static` metric and use it instead. +/// +/// Usage: +/// ``` +/// use wc_metrics::{enum_ordinalize::Ordinalize, BoolLabel, EnumLabel, StringLabel, histogram}; +/// +/// #[derive(Clone, Copy, Debug, Ordinalize)] +/// enum MyEnum { +/// A, +/// B, +/// } +/// +/// impl wc_metrics::Enum for MyEnum { +/// fn as_str(&self) -> &'static str { +/// match self { +/// Self::A => "a", +/// Self::B => "b", +/// } +/// } +/// } +/// +/// let s = "a"; +/// let b = true; +/// let u = 42; +/// let e = MyEnum::A; +/// +/// histogram!("name").record(1); +/// +/// histogram!("name", EnumLabel<"a", MyEnum> => e).record(1); +/// +/// histogram!("name", BoolLabel<"a"> => b).record(1); +/// +/// histogram!("name", StringLabel<"a"> => s).record(1); +/// +/// histogram!("name", StringLabel<"a", u8> => &u).record(1); +/// +/// histogram!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b +/// ) +/// .record(1); +/// +/// histogram!("name", "a" => "1").record(1); +/// +/// histogram!("name", "a" => "1", "b" => "2").record(1); +/// +/// histogram!("name", StringLabel<"a", u8> => &u, "b" => "2").record(1); +/// +/// histogram!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b, +/// "e" => "1", +/// "f" => "2" +/// ) +/// .record(1); +/// +/// histogram!("name", "description").record(1); +// +/// histogram!("name", "description", EnumLabel<"a", MyEnum> => e).record(1); +/// +/// histogram!("name", "description", BoolLabel<"a"> => b).record(1); +/// +/// histogram!("name", "description", StringLabel<"a"> => s).record(1); +/// +/// histogram!("name", "description", StringLabel<"a", u8> => &u).record(1); +/// +/// histogram!("name", "description", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b +/// ) +/// .record(1); +/// +/// histogram!("name", "description", "a" => "1").record(1); +/// +/// histogram!("name", "description", "a" => "1", "b" => "2").record(1); +/// +/// histogram!("name", "description", StringLabel<"a", u8> => &u, "b" => +/// "2").record(1); +/// +/// histogram!("name", "description", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b, +/// "e" => "1", +/// "f" => "2" +/// ) +/// .record(1); +/// ``` +#[macro_export] +macro_rules! histogram { + ($($tail:tt)*) => { + $crate::metric!($crate::backend::Histogram, $($tail)*) + }; +} + +/// Similar to [`counter`], [`gauge`] and [`histogram`], but operates with +/// [`FutureMetrics`](crate::FutureMetrics) instead. +/// +/// Usage: +/// ``` +/// use std::future::Future; +/// use wc_metrics::{enum_ordinalize::Ordinalize, BoolLabel, EnumLabel, StringLabel, future_metrics, FutureMetrics, FutureExt}; +/// +/// #[derive(Clone, Copy, Debug, Ordinalize)] +/// enum MyEnum { +/// A, +/// B, +/// } +/// +/// impl wc_metrics::Enum for MyEnum { +/// fn as_str(&self) -> &'static str { +/// match self { +/// Self::A => "a", +/// Self::B => "b", +/// } +/// } +/// } +/// +/// let s = "a"; +/// let b = true; +/// let u = 42; +/// let e = MyEnum::A; +/// +/// fn future(metrics: &'static FutureMetrics) -> impl Future { +/// async {}.with_metrics(metrics) +/// } +/// +/// fn spawn(f: impl Future) {} +/// +/// spawn(async {}.with_metrics(future_metrics!("name"))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", EnumLabel<"a", MyEnum> => e))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", BoolLabel<"a"> => b))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", StringLabel<"a"> => s))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", StringLabel<"a", u8> => &u))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b +/// ))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", "a" => "1"))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", "a" => "1", "b" => "2"))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", StringLabel<"a", u8> => &u, "b" => "2"))); +/// +/// spawn(async {}.with_metrics(future_metrics!("name", +/// EnumLabel<"a", MyEnum> => e, +/// StringLabel<"b"> => s, +/// StringLabel<"c", u8> => &u, +/// BoolLabel<"d"> => b, +/// "e" => "1", +/// "f" => "2" +/// ))); +/// ``` +#[cfg(feature = "future")] +#[macro_export] +macro_rules! future_metrics { + ($($tail:tt)*) => { + $crate::metric!($crate::FutureMetrics, $($tail)*) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! metric { + ( $type:ty, $name:literal) => { + { + static METRIC: $crate::Lazy<$type> = $crate::new($name); + &METRIC + } + }; + + ( $type:ty, $name:literal, $description:literal) => { + { + static METRIC: $crate::Lazy<$type> = $crate::builder($name) + .with_description($description) + .build(); + &METRIC + } + }; + + ( $type:ty, $name:literal, $description:literal, $($tail:tt)*) => { + { + static METRIC: $crate::Lazy<$crate::metric_type!($type, $($tail)*)> = $crate::builder($name) + .with_description($description) + .with_static_labels($crate::static_labels!($($tail)*)) + .build(); + + let m = &METRIC; + $crate::resolve_labels!(m, $($tail)*); + m + } + }; + ( $type:ty, $name:literal, $($tail:tt)*) => { + { + static METRIC: $crate::Lazy<$crate::metric_type!($type, $($tail)*)> = $crate::builder($name) + .with_static_labels($crate::static_labels!($($tail)*)) + .build(); + + let m = &METRIC; + $crate::resolve_labels!(m, $($tail)*); + m + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! metric_type { + ( $type:ty, $( $_:literal => $__:literal ),+ )=> { + $type + }; + ( $type:ty, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $( $_:literal => $__:literal ),+ )=> { + $crate::WithLabel<$label_type_name<{ $crate::label_name($label_name) }$(,$inner_ty)?>, $type> + }; + ( $type:ty, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $($tail:tt)*) => { + $crate::WithLabel<$label_type_name<{ $crate::label_name($label_name) }$(,$inner_ty)?>, $crate::metric_type!($type, $($tail)*)> + }; + ( $type:ty, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr )=> { + $crate::WithLabel<$label_type_name<{ $crate::label_name($label_name) }$(,$inner_ty)?>, $type> + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! static_labels { + ( $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr) => { + &[] + }; + ( $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $($tail:tt)*) => { + $crate::static_labels!($($tail)*) + }; + ( $( $label_name:literal => $label_value:literal ),+ ) => { + &[$(($label_name, $label_value),)*] + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! resolve_labels { + ( $var:ident, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr, $($tail:tt)*) => { + let $var = $var.resolve_label($label_type_name::<{ $crate::label_name($label_name) }$(,$inner_ty)?>::new($label_value)); + $crate::resolve_labels!($var, $($tail)*) + }; + ( $var:ident, $label_type_name:ident<$label_name:literal$(,$inner_ty:ty)?> => $label_value:expr )=> { + let $var = $var.resolve_label($label_type_name::<{ $crate::label_name($label_name) }$(,$inner_ty)?>::new($label_value)); + }; + ( $var:ident, $( $label_name:literal => $label_value:literal ),+ ) => {}; +}