diff --git a/src/counter.rs b/src/counter.rs index 1860695a..e0e67be3 100644 --- a/src/counter.rs +++ b/src/counter.rs @@ -44,10 +44,10 @@ impl GenericCounter

{ /// Create a [`GenericCounter`] with the `opts` options. pub fn with_opts(opts: Opts) -> Result { - Self::with_opts_and_label_values(&opts, &[]) + Self::with_opts_and_label_values::<&str>(&opts, &[]) } - fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result { + fn with_opts_and_label_values>(opts: &Opts, label_values: &[V]) -> Result { let v = Value::new(opts, ValueType::Counter, P::T::from_i64(0), label_values)?; Ok(Self { v: Arc::new(v) }) } @@ -126,7 +126,7 @@ impl MetricVecBuilder for CounterVecBuilder

{ type M = GenericCounter

; type P = Opts; - fn build(&self, opts: &Opts, vals: &[&str]) -> Result { + fn build>(&self, opts: &Opts, vals: &[V]) -> Result { Self::M::with_opts_and_label_values(opts, vals) } } @@ -452,6 +452,40 @@ mod tests { assert!(vec.remove(&labels3).is_err()); } + #[test] + fn test_counter_vec_with_owned_labels() { + let vec = CounterVec::new( + Opts::new("test_couter_vec", "test counter vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + + let mut labels = HashMap::new(); + labels.insert("l1", v1.clone()); + labels.insert("l2", v2.clone()); + assert!(vec.remove(&labels).is_err()); + + vec.with(&labels).inc(); + assert!(vec.remove(&labels).is_ok()); + assert!(vec.remove(&labels).is_err()); + + let mut labels2 = HashMap::new(); + labels2.insert("l1", v2.clone()); + labels2.insert("l2", v1.clone()); + + vec.with(&labels).inc(); + assert!(vec.remove(&labels2).is_err()); + + vec.with(&labels).inc(); + + let mut labels3 = HashMap::new(); + labels3.insert("l1", v1.clone()); + assert!(vec.remove(&labels3).is_err()); + } + #[test] fn test_int_counter_vec() { let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap(); @@ -491,6 +525,27 @@ mod tests { assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } + #[test] + fn test_counter_vec_with_owned_label_values() { + let vec = CounterVec::new( + Opts::new("test_vec", "test counter vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + let v3 = "v3".to_string(); + + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err()); + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok()); + + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + assert!(vec.remove_label_values(&[v1.clone()]).is_err()); + assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err()); + } + #[test] fn test_counter_vec_local() { let vec = CounterVec::new( diff --git a/src/gauge.rs b/src/gauge.rs index a6f7eb25..8e7f7028 100644 --- a/src/gauge.rs +++ b/src/gauge.rs @@ -43,10 +43,10 @@ impl GenericGauge

{ /// Create a [`GenericGauge`] with the `opts` options. pub fn with_opts(opts: Opts) -> Result { - Self::with_opts_and_label_values(&opts, &[]) + Self::with_opts_and_label_values::<&str>(&opts, &[]) } - fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result { + fn with_opts_and_label_values>(opts: &Opts, label_values: &[V]) -> Result { let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?; Ok(Self { v: Arc::new(v) }) } @@ -129,7 +129,7 @@ impl MetricVecBuilder for GaugeVecBuilder

{ type M = GenericGauge

; type P = Opts; - fn build(&self, opts: &Opts, vals: &[&str]) -> Result { + fn build>(&self, opts: &Opts, vals: &[V]) -> Result { Self::M::with_opts_and_label_values(opts, vals) } } @@ -218,6 +218,29 @@ mod tests { assert!(vec.remove(&labels).is_err()); } + #[test] + fn test_gauge_vec_with_owned_labels() { + let vec = GaugeVec::new( + Opts::new("test_gauge_vec", "test gauge vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let mut labels = HashMap::new(); + labels.insert("l1", "v1"); + labels.insert("l2", "v2"); + assert!(vec.remove(&labels).is_err()); + + vec.with(&labels).inc(); + vec.with(&labels).dec(); + vec.with(&labels).add(42.0); + vec.with(&labels).sub(42.0); + vec.with(&labels).set(42.0); + + assert!(vec.remove(&labels).is_ok()); + assert!(vec.remove(&labels).is_err()); + } + #[test] fn test_gauge_vec_with_label_values() { let vec = GaugeVec::new( @@ -239,4 +262,30 @@ mod tests { assert!(vec.remove_label_values(&["v1"]).is_err()); assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } + + #[test] + fn test_gauge_vec_with_owned_label_values() { + let vec = GaugeVec::new( + Opts::new("test_gauge_vec", "test gauge vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + let v3 = "v3".to_string(); + + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err()); + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok()); + + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + vec.with_label_values(&[v1.clone(), v2.clone()]).dec(); + vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0); + vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0); + vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0); + + assert!(vec.remove_label_values(&[v1.clone()]).is_err()); + assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err()); + } } diff --git a/src/histogram.rs b/src/histogram.rs index f58e401d..bf8cc5af 100644 --- a/src/histogram.rs +++ b/src/histogram.rs @@ -327,7 +327,7 @@ pub struct HistogramCore { } impl HistogramCore { - pub fn new(opts: &HistogramOpts, label_values: &[&str]) -> Result { + pub fn new>(opts: &HistogramOpts, label_values: &[V]) -> Result { let desc = opts.describe()?; for name in &desc.variable_labels { @@ -674,12 +674,12 @@ pub struct Histogram { impl Histogram { /// `with_opts` creates a [`Histogram`] with the `opts` options. pub fn with_opts(opts: HistogramOpts) -> Result { - Histogram::with_opts_and_label_values(&opts, &[]) + Histogram::with_opts_and_label_values::<&str>(&opts, &[]) } - fn with_opts_and_label_values( + fn with_opts_and_label_values>( opts: &HistogramOpts, - label_values: &[&str], + label_values: &[V], ) -> Result { let core = HistogramCore::new(opts, label_values)?; @@ -782,7 +782,7 @@ impl MetricVecBuilder for HistogramVecBuilder { type M = Histogram; type P = HistogramOpts; - fn build(&self, opts: &HistogramOpts, vals: &[&str]) -> Result { + fn build>(&self, opts: &HistogramOpts, vals: &[V]) -> Result { Histogram::with_opts_and_label_values(opts, vals) } } @@ -1389,6 +1389,27 @@ mod tests { assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } + #[test] + fn test_histogram_vec_with_owned_label_values() { + let vec = HistogramVec::new( + HistogramOpts::new("test_histogram_vec", "test histogram vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + let v3 = "v3".to_string(); + + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err()); + vec.with_label_values(&[v1.clone(), v2.clone()]) + .observe(1.0); + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok()); + + assert!(vec.remove_label_values(&[v1.clone()]).is_err()); + assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err()); + } + #[test] fn test_histogram_vec_with_opts_buckets() { let labels = ["l1", "l2"]; diff --git a/src/value.rs b/src/value.rs index 92640fe0..b9a9237e 100644 --- a/src/value.rs +++ b/src/value.rs @@ -37,11 +37,11 @@ pub struct Value { } impl Value

{ - pub fn new( + pub fn new>( describer: &D, val_type: ValueType, val: P::T, - label_values: &[&str], + label_values: &[V], ) -> Result { let desc = describer.describe()?; let label_pairs = make_label_pairs(&desc, label_values)?; @@ -114,7 +114,7 @@ impl Value

{ } } -pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result> { +pub fn make_label_pairs>(desc: &Desc, label_values: &[V]) -> Result> { if desc.variable_labels.len() != label_values.len() { return Err(Error::InconsistentCardinality { expect: desc.variable_labels.len(), @@ -135,7 +135,7 @@ pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result Result; + fn build>(&self, _: &Self::P, _: &[V]) -> Result; } #[derive(Debug)] @@ -49,7 +49,10 @@ impl MetricVecCore { m } - pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result { + pub fn get_metric_with_label_values(&self, vals: &[V]) -> Result + where + V: AsRef + std::fmt::Debug, + { let h = self.hash_label_values(vals)?; if let Some(metric) = self.children.read().get(&h).cloned() { @@ -59,7 +62,10 @@ impl MetricVecCore { self.get_or_create_metric(h, vals) } - pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result { + pub fn get_metric_with(&self, labels: &HashMap<&str, V>) -> Result + where + V: AsRef + std::fmt::Debug, + { let h = self.hash_labels(labels)?; if let Some(metric) = self.children.read().get(&h).cloned() { @@ -70,7 +76,10 @@ impl MetricVecCore { self.get_or_create_metric(h, &vals) } - pub fn delete_label_values(&self, vals: &[&str]) -> Result<()> { + pub fn delete_label_values(&self, vals: &[V]) -> Result<()> + where + V: AsRef + std::fmt::Debug, + { let h = self.hash_label_values(vals)?; let mut children = self.children.write(); @@ -81,7 +90,10 @@ impl MetricVecCore { Ok(()) } - pub fn delete(&self, labels: &HashMap<&str, &str>) -> Result<()> { + pub fn delete(&self, labels: &HashMap<&str, V>) -> Result<()> + where + V: AsRef + std::fmt::Debug, + { let h = self.hash_labels(labels)?; let mut children = self.children.write(); @@ -97,7 +109,10 @@ impl MetricVecCore { self.children.write().clear(); } - pub(crate) fn hash_label_values(&self, vals: &[&str]) -> Result { + pub(crate) fn hash_label_values(&self, vals: &[V]) -> Result + where + V: AsRef + std::fmt::Debug, + { if vals.len() != self.desc.variable_labels.len() { return Err(Error::InconsistentCardinality { expect: self.desc.variable_labels.len(), @@ -107,13 +122,16 @@ impl MetricVecCore { let mut h = FnvHasher::default(); for val in vals { - h.write(val.as_bytes()); + h.write(val.as_ref().as_bytes()); } Ok(h.finish()) } - fn hash_labels(&self, labels: &HashMap<&str, &str>) -> Result { + fn hash_labels(&self, labels: &HashMap<&str, V>) -> Result + where + V: AsRef + std::fmt::Debug, + { if labels.len() != self.desc.variable_labels.len() { return Err(Error::InconsistentCardinality { expect: self.desc.variable_labels.len(), @@ -124,7 +142,7 @@ impl MetricVecCore { let mut h = FnvHasher::default(); for name in &self.desc.variable_labels { match labels.get(&name.as_ref()) { - Some(val) => h.write(val.as_bytes()), + Some(val) => h.write(val.as_ref().as_bytes()), None => { return Err(Error::Msg(format!( "label name {} missing in label map", @@ -137,11 +155,14 @@ impl MetricVecCore { Ok(h.finish()) } - fn get_label_values<'a>(&self, labels: &'a HashMap<&str, &str>) -> Result> { + fn get_label_values<'a, V>(&'a self, labels: &'a HashMap<&str, V>) -> Result> + where + V: AsRef + std::fmt::Debug, + { let mut values = Vec::new(); for name in &self.desc.variable_labels { match labels.get(&name.as_ref()) { - Some(val) => values.push(*val), + Some(val) => values.push(val.as_ref()), None => { return Err(Error::Msg(format!( "label name {} missing in label map", @@ -153,7 +174,10 @@ impl MetricVecCore { Ok(values) } - fn get_or_create_metric(&self, hash: u64, label_values: &[&str]) -> Result { + fn get_or_create_metric(&self, hash: u64, label_values: &[V]) -> Result + where + V: AsRef + std::fmt::Debug, + { let mut children = self.children.write(); // Check exist first. if let Some(metric) = children.get(&hash).cloned() { @@ -221,7 +245,10 @@ impl MetricVec { /// an alternative to avoid that type of mistake. For higher label numbers, the /// latter has a much more readable (albeit more verbose) syntax, but it comes /// with a performance overhead (for creating and processing the Labels map). - pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result { + pub fn get_metric_with_label_values(&self, vals: &[V]) -> Result + where + V: AsRef + std::fmt::Debug, + { self.v.get_metric_with_label_values(vals) } @@ -237,7 +264,10 @@ impl MetricVec { /// This method is used for the same purpose as /// `get_metric_with_label_values`. See there for pros and cons of the two /// methods. - pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result { + pub fn get_metric_with(&self, labels: &HashMap<&str, V>) -> Result + where + V: AsRef + std::fmt::Debug, + { self.v.get_metric_with(labels) } @@ -254,14 +284,20 @@ impl MetricVec { /// ).unwrap(); /// vec.with_label_values(&["404", "POST"]).inc() /// ``` - pub fn with_label_values(&self, vals: &[&str]) -> T::M { + pub fn with_label_values(&self, vals: &[V]) -> T::M + where + V: AsRef + std::fmt::Debug, + { self.get_metric_with_label_values(vals).unwrap() } /// `with` works as `get_metric_with`, but panics if an error occurs. The method allows /// neat syntax like: /// httpReqs.with(Labels{"status":"404", "method":"POST"}).inc() - pub fn with(&self, labels: &HashMap<&str, &str>) -> T::M { + pub fn with(&self, labels: &HashMap<&str, V>) -> T::M + where + V: AsRef + std::fmt::Debug, + { self.get_metric_with(labels).unwrap() } @@ -277,7 +313,10 @@ impl MetricVec { /// alternative to avoid that type of mistake. For higher label numbers, the /// latter has a much more readable (albeit more verbose) syntax, but it comes /// with a performance overhead (for creating and processing the Labels map). - pub fn remove_label_values(&self, vals: &[&str]) -> Result<()> { + pub fn remove_label_values(&self, vals: &[V]) -> Result<()> + where + V: AsRef + std::fmt::Debug, + { self.v.delete_label_values(vals) } @@ -289,7 +328,10 @@ impl MetricVec { /// /// This method is used for the same purpose as `delete_label_values`. See /// there for pros and cons of the two methods. - pub fn remove(&self, labels: &HashMap<&str, &str>) -> Result<()> { + pub fn remove(&self, labels: &HashMap<&str, V>) -> Result<()> + where + V: AsRef + std::fmt::Debug, + { self.v.delete(labels) } @@ -348,6 +390,40 @@ mod tests { assert!(vec.remove(&labels3).is_err()); } + #[test] + fn test_counter_vec_with_owned_labels() { + let vec = CounterVec::new( + Opts::new("test_couter_vec", "test counter vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + + let mut labels = HashMap::new(); + labels.insert("l1", v1.clone()); + labels.insert("l2", v2.clone()); + assert!(vec.remove(&labels).is_err()); + + vec.with(&labels).inc(); + assert!(vec.remove(&labels).is_ok()); + assert!(vec.remove(&labels).is_err()); + + let mut labels2 = HashMap::new(); + labels2.insert("l1", v2.clone()); + labels2.insert("l2", v1.clone()); + + vec.with(&labels).inc(); + assert!(vec.remove(&labels2).is_err()); + + vec.with(&labels).inc(); + + let mut labels3 = HashMap::new(); + labels3.insert("l1", v1.clone()); + assert!(vec.remove(&labels3).is_err()); + } + #[test] fn test_counter_vec_with_label_values() { let vec = CounterVec::new( @@ -365,6 +441,27 @@ mod tests { assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } + #[test] + fn test_counter_vec_with_owned_label_values() { + let vec = CounterVec::new( + Opts::new("test_vec", "test counter vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + let v3 = "v3".to_string(); + + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err()); + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok()); + + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + assert!(vec.remove_label_values(&[v1.clone()]).is_err()); + assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err()); + } + #[test] fn test_gauge_vec_with_labels() { let vec = GaugeVec::new( @@ -388,6 +485,32 @@ mod tests { assert!(vec.remove(&labels).is_err()); } + #[test] + fn test_gauge_vec_with_owned_labels() { + let vec = GaugeVec::new( + Opts::new("test_gauge_vec", "test gauge vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + + let mut labels = HashMap::new(); + labels.insert("l1", v1.clone()); + labels.insert("l2", v2.clone()); + assert!(vec.remove(&labels).is_err()); + + vec.with(&labels).inc(); + vec.with(&labels).dec(); + vec.with(&labels).add(42.0); + vec.with(&labels).sub(42.0); + vec.with(&labels).set(42.0); + + assert!(vec.remove(&labels).is_ok()); + assert!(vec.remove(&labels).is_err()); + } + #[test] fn test_gauge_vec_with_label_values() { let vec = GaugeVec::new( @@ -410,6 +533,32 @@ mod tests { assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } + #[test] + fn test_gauge_vec_with_owned_label_values() { + let vec = GaugeVec::new( + Opts::new("test_gauge_vec", "test gauge vec help"), + &["l1", "l2"], + ) + .unwrap(); + + let v1 = "v1".to_string(); + let v2 = "v2".to_string(); + let v3 = "v3".to_string(); + + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err()); + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok()); + + vec.with_label_values(&[v1.clone(), v2.clone()]).inc(); + vec.with_label_values(&[v1.clone(), v2.clone()]).dec(); + vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0); + vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0); + vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0); + + assert!(vec.remove_label_values(&[v1.clone()]).is_err()); + assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err()); + } + #[test] fn test_vec_get_metric_with() { let vec = CounterVec::new(