summaryrefslogtreecommitdiffstats
path: root/third_party/rust/glean/src/private
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/glean/src/private')
-rw-r--r--third_party/rust/glean/src/private/boolean.rs49
-rw-r--r--third_party/rust/glean/src/private/counter.rs62
-rw-r--r--third_party/rust/glean/src/private/custom_distribution.rs81
-rw-r--r--third_party/rust/glean/src/private/datetime.rs103
-rw-r--r--third_party/rust/glean/src/private/event.rs173
-rw-r--r--third_party/rust/glean/src/private/labeled.rs355
-rw-r--r--third_party/rust/glean/src/private/memory_distribution.rs67
-rw-r--r--third_party/rust/glean/src/private/mod.rs37
-rw-r--r--third_party/rust/glean/src/private/ping.rs48
-rw-r--r--third_party/rust/glean/src/private/quantity.rs63
-rw-r--r--third_party/rust/glean/src/private/recorded_experiment_data.rs15
-rw-r--r--third_party/rust/glean/src/private/string.rs72
-rw-r--r--third_party/rust/glean/src/private/string_list.rs108
-rw-r--r--third_party/rust/glean/src/private/timespan.rs163
-rw-r--r--third_party/rust/glean/src/private/timing_distribution.rs99
-rw-r--r--third_party/rust/glean/src/private/uuid.rs69
16 files changed, 1564 insertions, 0 deletions
diff --git a/third_party/rust/glean/src/private/boolean.rs b/third_party/rust/glean/src/private/boolean.rs
new file mode 100644
index 0000000000..c9bc87535d
--- /dev/null
+++ b/third_party/rust/glean/src/private/boolean.rs
@@ -0,0 +1,49 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording boolean metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct BooleanMetric(pub(crate) Arc<glean_core::metrics::BooleanMetric>);
+
+impl BooleanMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ Self(Arc::new(glean_core::metrics::BooleanMetric::new(meta)))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::Boolean for BooleanMetric {
+ fn set(&self, value: bool) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<bool> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = match ping_name.into() {
+ Some(name) => name,
+ None => self.0.meta().send_in_pings.first().unwrap(),
+ };
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+}
diff --git a/third_party/rust/glean/src/private/counter.rs b/third_party/rust/glean/src/private/counter.rs
new file mode 100644
index 0000000000..61e4cc3242
--- /dev/null
+++ b/third_party/rust/glean/src/private/counter.rs
@@ -0,0 +1,62 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording counter metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct CounterMetric(pub(crate) Arc<glean_core::metrics::CounterMetric>);
+
+impl CounterMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ Self(Arc::new(glean_core::metrics::CounterMetric::new(meta)))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::Counter for CounterMetric {
+ fn add(&self, amount: i32) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, amount)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i32> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean/src/private/custom_distribution.rs b/third_party/rust/glean/src/private/custom_distribution.rs
new file mode 100644
index 0000000000..790850c8d7
--- /dev/null
+++ b/third_party/rust/glean/src/private/custom_distribution.rs
@@ -0,0 +1,81 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::{DistributionData, MetricType};
+use glean_core::{CommonMetricData, ErrorType, HistogramType};
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer-facing API for recording custom distribution metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct CustomDistributionMetric(pub(crate) Arc<glean_core::metrics::CustomDistributionMetric>);
+
+impl CustomDistributionMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(
+ meta: CommonMetricData,
+ range_min: u64,
+ range_max: u64,
+ bucket_count: u64,
+ histogram_type: HistogramType,
+ ) -> Self {
+ Self(Arc::new(
+ glean_core::metrics::CustomDistributionMetric::new(
+ meta,
+ range_min,
+ range_max,
+ bucket_count,
+ histogram_type,
+ ),
+ ))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::CustomDistribution for CustomDistributionMetric {
+ fn accumulate_samples_signed(&self, samples: Vec<i64>) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || {
+ crate::with_glean(|glean| metric.accumulate_samples_signed(glean, samples))
+ });
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ ping_name: S,
+ ) -> Option<DistributionData> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean/src/private/datetime.rs b/third_party/rust/glean/src/private/datetime.rs
new file mode 100644
index 0000000000..fcb6376022
--- /dev/null
+++ b/third_party/rust/glean/src/private/datetime.rs
@@ -0,0 +1,103 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+pub use glean_core::metrics::{Datetime, TimeUnit};
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording Datetime metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct DatetimeMetric(pub(crate) Arc<glean_core::metrics::DatetimeMetric>);
+
+impl DatetimeMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self {
+ Self(Arc::new(glean_core::metrics::DatetimeMetric::new(
+ meta, time_unit,
+ )))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::Datetime for DatetimeMetric {
+ fn set(&self, value: Option<Datetime>) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<Datetime> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::common_test::{lock_test, new_glean};
+ use crate::CommonMetricData;
+ use chrono::prelude::*;
+
+ #[test]
+ fn datetime_convenient_api() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ let metric: DatetimeMetric = DatetimeMetric::new(
+ CommonMetricData {
+ name: "datetime".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ TimeUnit::Day,
+ );
+
+ // Record a date: it will get truncated to Day resolution.
+ let sample_date = FixedOffset::east(0).ymd(2018, 2, 25).and_hms(11, 5, 0);
+ metric.set(Some(sample_date));
+
+ // Check that the value has the correct resolution.
+ let date = metric.test_get_value(None).unwrap();
+ assert_eq!(date, FixedOffset::east(0).ymd(2018, 2, 25).and_hms(0, 0, 0));
+
+ // Ensure no error was recorded.
+ assert_eq!(
+ metric.test_get_num_recorded_errors(ErrorType::InvalidValue, None),
+ 0
+ )
+ }
+}
diff --git a/third_party/rust/glean/src/private/event.rs b/third_party/rust/glean/src/private/event.rs
new file mode 100644
index 0000000000..7bc6fe1017
--- /dev/null
+++ b/third_party/rust/glean/src/private/event.rs
@@ -0,0 +1,173 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::{collections::HashMap, marker::PhantomData, sync::Arc};
+
+use glean_core::metrics::MetricType;
+use glean_core::traits;
+
+use crate::{dispatcher, ErrorType, RecordedEvent};
+
+pub use glean_core::traits::NoExtraKeys;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording event metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct EventMetric<K> {
+ pub(crate) inner: Arc<glean_core::metrics::EventMetric>,
+ extra_keys: PhantomData<K>,
+}
+
+impl<K: traits::ExtraKeys> EventMetric<K> {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ let allowed_extra_keys = K::ALLOWED_KEYS.iter().map(|s| s.to_string()).collect();
+ let inner = Arc::new(glean_core::metrics::EventMetric::new(
+ meta,
+ allowed_extra_keys,
+ ));
+ Self {
+ inner,
+ extra_keys: PhantomData,
+ }
+ }
+}
+
+#[inherent(pub)]
+impl<K: traits::ExtraKeys> traits::Event for EventMetric<K> {
+ type Extra = K;
+
+ fn record<M: Into<Option<HashMap<<Self as traits::Event>::Extra, String>>>>(&self, extra: M) {
+ const NANOS_PER_MILLI: u64 = 1_000_000;
+ let now = time::precise_time_ns() / NANOS_PER_MILLI;
+
+ // Translate from [ExtraKey -> String] to a [Int -> String] map
+ let extra = extra
+ .into()
+ .map(|h| h.into_iter().map(|(k, v)| (k.index(), v)).collect());
+ let metric = Arc::clone(&self.inner);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.record(glean, now, extra)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ ping_name: S,
+ ) -> Option<Vec<RecordedEvent>> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.inner.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.inner.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(
+ &glean,
+ self.inner.meta(),
+ error,
+ ping_name.into(),
+ )
+ .unwrap_or(0)
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::common_test::{lock_test, new_glean};
+ use crate::CommonMetricData;
+
+ #[test]
+ fn no_extra_keys() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ let metric: EventMetric<NoExtraKeys> = EventMetric::new(CommonMetricData {
+ name: "event".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ });
+
+ metric.record(None);
+ metric.record(None);
+
+ let data = metric.test_get_value(None).expect("no event recorded");
+ assert_eq!(2, data.len());
+ assert!(data[0].timestamp <= data[1].timestamp);
+ }
+
+ #[test]
+ fn with_extra_keys() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+ enum SomeExtra {
+ Key1,
+ Key2,
+ }
+
+ impl glean_core::traits::ExtraKeys for SomeExtra {
+ const ALLOWED_KEYS: &'static [&'static str] = &["key1", "key2"];
+
+ fn index(self) -> i32 {
+ self as i32
+ }
+ }
+
+ let metric: EventMetric<SomeExtra> = EventMetric::new(CommonMetricData {
+ name: "event".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ });
+
+ let mut map1 = HashMap::new();
+ map1.insert(SomeExtra::Key1, "1".into());
+ metric.record(map1);
+
+ let mut map2 = HashMap::new();
+ map2.insert(SomeExtra::Key1, "1".into());
+ map2.insert(SomeExtra::Key2, "2".into());
+ metric.record(map2);
+
+ metric.record(None);
+
+ let data = metric.test_get_value(None).expect("no event recorded");
+ assert_eq!(3, data.len());
+ assert!(data[0].timestamp <= data[1].timestamp);
+ assert!(data[1].timestamp <= data[2].timestamp);
+
+ let mut map = HashMap::new();
+ map.insert("key1".into(), "1".into());
+ assert_eq!(Some(map), data[0].extra);
+
+ let mut map = HashMap::new();
+ map.insert("key1".into(), "1".into());
+ map.insert("key2".into(), "2".into());
+ assert_eq!(Some(map), data[1].extra);
+
+ assert_eq!(None, data[2].extra);
+ }
+}
diff --git a/third_party/rust/glean/src/private/labeled.rs b/third_party/rust/glean/src/private/labeled.rs
new file mode 100644
index 0000000000..af4f1411a3
--- /dev/null
+++ b/third_party/rust/glean/src/private/labeled.rs
@@ -0,0 +1,355 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+use glean_core::ErrorType;
+
+/// Sealed traits protect against downstream implementations.
+///
+/// We wrap it in a private module that is inaccessible outside of this module.
+mod private {
+ use crate::{
+ private::BooleanMetric, private::CounterMetric, private::StringMetric, CommonMetricData,
+ };
+ use std::sync::Arc;
+
+ /// The sealed labeled trait.
+ ///
+ /// This also allows us to hide methods, that are only used internally
+ /// and should not be visible to users of the object implementing the
+ /// `Labeled<T>` trait.
+ pub trait Sealed {
+ /// The `glean_core` metric type representing the labeled metric.
+ type Inner: glean_core::metrics::MetricType + Clone;
+
+ /// Create a new metric object implementing this trait from the inner type.
+ fn from_inner(metric: Self::Inner) -> Self;
+
+ /// Create a new `glean_core` metric from the metadata.
+ fn new_inner(meta: crate::CommonMetricData) -> Self::Inner;
+ }
+
+ // `LabeledMetric<BooleanMetric>` is possible.
+ //
+ // See [Labeled Booleans](https://mozilla.github.io/glean/book/user/metrics/labeled_booleans.html).
+ impl Sealed for BooleanMetric {
+ type Inner = glean_core::metrics::BooleanMetric;
+
+ fn from_inner(metric: Self::Inner) -> Self {
+ BooleanMetric(Arc::new(metric))
+ }
+
+ fn new_inner(meta: CommonMetricData) -> Self::Inner {
+ glean_core::metrics::BooleanMetric::new(meta)
+ }
+ }
+
+ // `LabeledMetric<StringMetric>` is possible.
+ //
+ // See [Labeled Strings](https://mozilla.github.io/glean/book/user/metrics/labeled_strings.html).
+ impl Sealed for StringMetric {
+ type Inner = glean_core::metrics::StringMetric;
+
+ fn from_inner(metric: Self::Inner) -> Self {
+ StringMetric(Arc::new(metric))
+ }
+
+ fn new_inner(meta: CommonMetricData) -> Self::Inner {
+ glean_core::metrics::StringMetric::new(meta)
+ }
+ }
+
+ // `LabeledMetric<CounterMetric>` is possible.
+ //
+ // See [Labeled Counters](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html).
+ impl Sealed for CounterMetric {
+ type Inner = glean_core::metrics::CounterMetric;
+
+ fn from_inner(metric: Self::Inner) -> Self {
+ CounterMetric(Arc::new(metric))
+ }
+
+ fn new_inner(meta: CommonMetricData) -> Self::Inner {
+ glean_core::metrics::CounterMetric::new(meta)
+ }
+ }
+}
+
+/// Marker trait for metrics that can be nested inside a labeled metric.
+///
+/// This trait is sealed and cannot be implemented for types outside this crate.
+pub trait AllowLabeled: private::Sealed {}
+
+// Implement the trait for everything we marked as allowed.
+impl<T> AllowLabeled for T where T: private::Sealed {}
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the specific facing API for recording labeled metrics.
+///
+/// Instances of this type are automatically generated by the parser
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+/// Unlike most metric types, [`LabeledMetric`] does not have its own corresponding
+/// storage, but records metrics for the underlying metric type `T` in the storage
+/// for that type.
+#[derive(Clone)]
+pub struct LabeledMetric<T: AllowLabeled>(
+ pub(crate) Arc<glean_core::metrics::LabeledMetric<T::Inner>>,
+);
+
+impl<T> LabeledMetric<T>
+where
+ T: AllowLabeled,
+{
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData, labels: Option<Vec<String>>) -> Self {
+ let submetric = T::new_inner(meta);
+ let core = glean_core::metrics::LabeledMetric::new(submetric, labels);
+ Self(Arc::new(core))
+ }
+}
+
+#[inherent(pub)]
+impl<T> glean_core::traits::Labeled<T> for LabeledMetric<T>
+where
+ T: AllowLabeled + Clone,
+{
+ fn get(&self, label: &str) -> T {
+ let inner = self.0.get(label);
+ T::from_inner(inner)
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(
+ &glean,
+ self.0.get_submetric().meta(),
+ error,
+ ping_name.into(),
+ )
+ .unwrap_or(0)
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::ErrorType;
+ use crate::common_test::{lock_test, new_glean};
+ use crate::destroy_glean;
+ use crate::private::{BooleanMetric, CounterMetric, LabeledMetric, StringMetric};
+ use crate::CommonMetricData;
+
+ #[test]
+ fn test_labeled_counter_type() {
+ let _lock = lock_test();
+
+ let _t = new_glean(None, true);
+
+ let metric: LabeledMetric<CounterMetric> = LabeledMetric::new(
+ CommonMetricData {
+ name: "labeled_counter".into(),
+ category: "labeled".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ None,
+ );
+
+ metric.get("label1").add(1);
+ metric.get("label2").add(2);
+ assert_eq!(1, metric.get("label1").test_get_value("test1").unwrap());
+ assert_eq!(2, metric.get("label2").test_get_value("test1").unwrap());
+ }
+
+ #[test]
+ fn test_other_label_with_predefined_labels() {
+ let _lock = lock_test();
+
+ let _t = new_glean(None, true);
+
+ let metric: LabeledMetric<CounterMetric> = LabeledMetric::new(
+ CommonMetricData {
+ name: "labeled_counter".into(),
+ category: "labeled".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ Some(vec!["foo".into(), "bar".into(), "baz".into()]),
+ );
+
+ metric.get("foo").add(1);
+ metric.get("foo").add(2);
+ metric.get("bar").add(1);
+ metric.get("not_there").add(1);
+ metric.get("also_not_there").add(1);
+ metric.get("not_me").add(1);
+
+ assert_eq!(3, metric.get("foo").test_get_value(None).unwrap());
+ assert_eq!(1, metric.get("bar").test_get_value(None).unwrap());
+ assert!(metric.get("baz").test_get_value(None).is_none());
+ // The rest all lands in the __other__ bucket.
+ assert_eq!(3, metric.get("__other__").test_get_value(None).unwrap());
+ }
+
+ #[test]
+ fn test_other_label_without_predefined_labels() {
+ let _lock = lock_test();
+
+ let _t = new_glean(None, true);
+
+ let metric: LabeledMetric<CounterMetric> = LabeledMetric::new(
+ CommonMetricData {
+ name: "labeled_counter".into(),
+ category: "labeled".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ None,
+ );
+
+ // Record in 20 labels: it will go over the maximum number of supported
+ // dynamic labels.
+ for i in 0..=20 {
+ metric.get(format!("label_{}", i).as_str()).add(1);
+ }
+ // Record in a label once again.
+ metric.get("label_0").add(1);
+
+ assert_eq!(2, metric.get("label_0").test_get_value(None).unwrap());
+ for i in 1..15 {
+ assert_eq!(
+ 1,
+ metric
+ .get(format!("label_{}", i).as_str())
+ .test_get_value(None)
+ .unwrap()
+ );
+ }
+ assert_eq!(5, metric.get("__other__").test_get_value(None).unwrap());
+ }
+
+ #[test]
+ fn test_other_label_without_predefined_labels_before_glean_init() {
+ let _lock = lock_test();
+
+ // We explicitly want Glean to not be initialized.
+ destroy_glean(true);
+
+ let metric: LabeledMetric<CounterMetric> = LabeledMetric::new(
+ CommonMetricData {
+ name: "labeled_counter".into(),
+ category: "labeled".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ None,
+ );
+
+ // Record in 20 labels: it will go over the maximum number of supported
+ // dynamic labels.
+ for i in 0..=20 {
+ metric.get(format!("label_{}", i).as_str()).add(1);
+ }
+ // Record in a label once again.
+ metric.get("label_0").add(1);
+
+ // Initialize Glean.
+ let _t = new_glean(None, false);
+
+ assert_eq!(2, metric.get("label_0").test_get_value(None).unwrap());
+ for i in 1..15 {
+ assert_eq!(
+ 1,
+ metric
+ .get(format!("label_{}", i).as_str())
+ .test_get_value(None)
+ .unwrap()
+ );
+ }
+ assert_eq!(5, metric.get("__other__").test_get_value(None).unwrap());
+ }
+
+ #[test]
+ fn test_labeled_string_type() {
+ let _lock = lock_test();
+
+ let _t = new_glean(None, true);
+
+ let metric: LabeledMetric<StringMetric> = LabeledMetric::new(
+ CommonMetricData {
+ name: "labeled_string".into(),
+ category: "labeled".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ None,
+ );
+
+ metric.get("label1").set("foo");
+ metric.get("label2").set("bar");
+ assert_eq!("foo", metric.get("label1").test_get_value("test1").unwrap());
+ assert_eq!("bar", metric.get("label2").test_get_value("test1").unwrap());
+ }
+
+ #[test]
+ fn test_labeled_boolean_type() {
+ let _lock = lock_test();
+
+ let _t = new_glean(None, true);
+
+ let metric: LabeledMetric<BooleanMetric> = LabeledMetric::new(
+ CommonMetricData {
+ name: "labeled_boolean".into(),
+ category: "labeled".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ None,
+ );
+
+ metric.get("label1").set(false);
+ metric.get("label2").set(true);
+ assert!(!metric.get("label1").test_get_value("test1").unwrap());
+ assert!(metric.get("label2").test_get_value("test1").unwrap());
+ }
+
+ #[test]
+ fn test_invalid_labels_record_errors() {
+ let _lock = lock_test();
+
+ let _t = new_glean(None, true);
+
+ let metric: LabeledMetric<BooleanMetric> = LabeledMetric::new(
+ CommonMetricData {
+ name: "labeled_boolean".into(),
+ category: "labeled".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ None,
+ );
+
+ let invalid_label = "!#I'm invalid#--_";
+ metric.get(invalid_label).set(true);
+ assert_eq!(true, metric.get("__other__").test_get_value(None).unwrap());
+ assert_eq!(
+ 1,
+ metric.test_get_num_recorded_errors(ErrorType::InvalidLabel, None)
+ );
+ }
+}
diff --git a/third_party/rust/glean/src/private/memory_distribution.rs b/third_party/rust/glean/src/private/memory_distribution.rs
new file mode 100644
index 0000000000..584d86c9d1
--- /dev/null
+++ b/third_party/rust/glean/src/private/memory_distribution.rs
@@ -0,0 +1,67 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::{DistributionData, MemoryUnit, MetricType};
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer-facing API for recording memory distribution metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct MemoryDistributionMetric(pub(crate) Arc<glean_core::metrics::MemoryDistributionMetric>);
+
+impl MemoryDistributionMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData, memory_unit: MemoryUnit) -> Self {
+ Self(Arc::new(
+ glean_core::metrics::MemoryDistributionMetric::new(meta, memory_unit),
+ ))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::MemoryDistribution for MemoryDistributionMetric {
+ fn accumulate(&self, sample: u64) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.accumulate(glean, sample)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ ping_name: S,
+ ) -> Option<DistributionData> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean/src/private/mod.rs b/third_party/rust/glean/src/private/mod.rs
new file mode 100644
index 0000000000..c4b692072b
--- /dev/null
+++ b/third_party/rust/glean/src/private/mod.rs
@@ -0,0 +1,37 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+//! The different metric types supported by the Glean SDK to handle data.
+
+mod boolean;
+mod counter;
+mod custom_distribution;
+mod datetime;
+mod event;
+mod labeled;
+mod memory_distribution;
+mod ping;
+mod quantity;
+mod recorded_experiment_data;
+mod string;
+mod string_list;
+mod timespan;
+mod timing_distribution;
+mod uuid;
+
+pub use self::uuid::UuidMetric;
+pub use boolean::BooleanMetric;
+pub use counter::CounterMetric;
+pub use custom_distribution::CustomDistributionMetric;
+pub use datetime::{Datetime, DatetimeMetric};
+pub use event::EventMetric;
+pub use labeled::{AllowLabeled, LabeledMetric};
+pub use memory_distribution::MemoryDistributionMetric;
+pub use ping::PingType;
+pub use quantity::QuantityMetric;
+pub use recorded_experiment_data::RecordedExperimentData;
+pub use string::StringMetric;
+pub use string_list::StringListMetric;
+pub use timespan::TimespanMetric;
+pub use timing_distribution::TimingDistributionMetric;
diff --git a/third_party/rust/glean/src/private/ping.rs b/third_party/rust/glean/src/private/ping.rs
new file mode 100644
index 0000000000..c9452b9d35
--- /dev/null
+++ b/third_party/rust/glean/src/private/ping.rs
@@ -0,0 +1,48 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+
+/// A Glean ping.
+#[derive(Clone, Debug)]
+pub struct PingType {
+ pub(crate) name: String,
+ pub(crate) ping_type: glean_core::metrics::PingType,
+}
+
+impl PingType {
+ /// Creates a new ping type.
+ ///
+ /// # Arguments
+ ///
+ /// * `name` - The name of the ping.
+ /// * `include_client_id` - Whether to include the client ID in the assembled ping when.
+ /// * `send_if_empty` - Whether the ping should be sent empty or not.
+ /// * `reason_codes` - The valid reason codes for this ping.
+ pub fn new<A: Into<String>>(
+ name: A,
+ include_client_id: bool,
+ send_if_empty: bool,
+ reason_codes: Vec<String>,
+ ) -> Self {
+ let name = name.into();
+ let ping_type = glean_core::metrics::PingType::new(
+ name.clone(),
+ include_client_id,
+ send_if_empty,
+ reason_codes,
+ );
+
+ let me = Self { name, ping_type };
+ crate::register_ping_type(&me);
+ me
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::Ping for PingType {
+ fn submit(&self, reason: Option<&str>) {
+ crate::submit_ping(self, reason)
+ }
+}
diff --git a/third_party/rust/glean/src/private/quantity.rs b/third_party/rust/glean/src/private/quantity.rs
new file mode 100644
index 0000000000..716ce5147c
--- /dev/null
+++ b/third_party/rust/glean/src/private/quantity.rs
@@ -0,0 +1,63 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type, otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording Quantity metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct QuantityMetric(pub(crate) Arc<glean_core::metrics::QuantityMetric>);
+
+impl QuantityMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ Self(Arc::new(glean_core::metrics::QuantityMetric::new(meta)))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::Quantity for QuantityMetric {
+ fn set(&self, value: i64) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i64> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ #[allow(dead_code)] // Remove after mozilla/glean#1328
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean/src/private/recorded_experiment_data.rs b/third_party/rust/glean/src/private/recorded_experiment_data.rs
new file mode 100644
index 0000000000..0550b536a7
--- /dev/null
+++ b/third_party/rust/glean/src/private/recorded_experiment_data.rs
@@ -0,0 +1,15 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use crate::HashMap;
+use serde::Deserialize;
+
+/// Deserialized experiment data.
+#[derive(Clone, Deserialize, Debug)]
+pub struct RecordedExperimentData {
+ /// The experiment's branch as set through [`set_experiment_active`](crate::set_experiment_active).
+ pub branch: String,
+ /// Any extra data associated with this experiment through [`set_experiment_active`](crate::set_experiment_active).
+ pub extra: Option<HashMap<String, String>>,
+}
diff --git a/third_party/rust/glean/src/private/string.rs b/third_party/rust/glean/src/private/string.rs
new file mode 100644
index 0000000000..0f11a016a9
--- /dev/null
+++ b/third_party/rust/glean/src/private/string.rs
@@ -0,0 +1,72 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use glean_core::Glean;
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type, otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording string metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct StringMetric(pub(crate) Arc<glean_core::metrics::StringMetric>);
+
+impl StringMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ Self(Arc::new(glean_core::metrics::StringMetric::new(meta)))
+ }
+
+ /// Internal only, synchronous API for setting a string value.
+ pub(crate) fn set_sync<S: Into<std::string::String>>(&self, glean: &Glean, value: S) {
+ self.0.set(glean, value);
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::String for StringMetric {
+ fn set<S: Into<std::string::String>>(&self, value: S) {
+ let metric = Arc::clone(&self.0);
+ let new_value = value.into();
+ dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, new_value)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ ping_name: S,
+ ) -> Option<std::string::String> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean/src/private/string_list.rs b/third_party/rust/glean/src/private/string_list.rs
new file mode 100644
index 0000000000..e37bec4fa6
--- /dev/null
+++ b/third_party/rust/glean/src/private/string_list.rs
@@ -0,0 +1,108 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer-facing API for recording string list metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct StringListMetric(pub(crate) Arc<glean_core::metrics::StringListMetric>);
+
+impl StringListMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ Self(Arc::new(glean_core::metrics::StringListMetric::new(meta)))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::StringList for StringListMetric {
+ fn add<S: Into<String>>(&self, value: S) {
+ let metric = Arc::clone(&self.0);
+ let new_value = value.into();
+ dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, new_value)));
+ }
+
+ fn set(&self, value: Vec<String>) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<Vec<String>> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::common_test::{lock_test, new_glean};
+ use crate::{CommonMetricData, ErrorType};
+
+ #[test]
+ fn string_list_metric_docs() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ let engine_metric: StringListMetric = StringListMetric::new(CommonMetricData {
+ name: "event".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ });
+
+ let engines: Vec<String> = vec!["Google".to_string(), "DuckDuckGo".to_string()];
+
+ // Add them one at a time
+ engines.iter().for_each(|x| engine_metric.add(x));
+
+ // Set them in one go
+ engine_metric.set(engines);
+
+ assert!(engine_metric.test_get_value(None).is_some());
+
+ assert_eq!(
+ vec!["Google".to_string(), "DuckDuckGo".to_string()],
+ engine_metric.test_get_value(None).unwrap()
+ );
+
+ assert_eq!(
+ 0,
+ engine_metric.test_get_num_recorded_errors(ErrorType::InvalidValue, None)
+ );
+ }
+}
diff --git a/third_party/rust/glean/src/private/timespan.rs b/third_party/rust/glean/src/private/timespan.rs
new file mode 100644
index 0000000000..111d3b2d4a
--- /dev/null
+++ b/third_party/rust/glean/src/private/timespan.rs
@@ -0,0 +1,163 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use std::sync::{Arc, RwLock};
+
+use inherent::inherent;
+
+use glean_core::metrics::{MetricType, TimeUnit};
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording timespan metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct TimespanMetric(pub(crate) Arc<RwLock<glean_core::metrics::TimespanMetric>>);
+
+impl TimespanMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self {
+ let timespan = glean_core::metrics::TimespanMetric::new(meta, time_unit);
+ Self(Arc::new(RwLock::new(timespan)))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::Timespan for TimespanMetric {
+ fn start(&self) {
+ let start_time = time::precise_time_ns();
+
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || {
+ crate::with_glean(|glean| {
+ let mut lock = metric
+ .write()
+ .expect("Lock poisoned for timespan metric on start.");
+ lock.set_start(glean, start_time)
+ })
+ });
+ }
+
+ fn stop(&self) {
+ let stop_time = time::precise_time_ns();
+
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || {
+ crate::with_glean(|glean| {
+ let mut lock = metric
+ .write()
+ .expect("Lock poisoned for timespan metric on stop.");
+ lock.set_stop(glean, stop_time)
+ })
+ });
+ }
+
+ fn cancel(&self) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || {
+ let mut lock = metric
+ .write()
+ .expect("Lock poisoned for timespan metric on cancel.");
+ lock.cancel()
+ });
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<u64> {
+ crate::block_on_dispatcher();
+
+ crate::with_glean(|glean| {
+ // Note: The order of operations is important here to avoid potential deadlocks because
+ // of `lock-order-inversion`.
+ // `with_glean` takes a lock on the global Glean object,
+ // then we take a lock on the metric itself here.
+ //
+ // Other parts do it in the same order, see for example `start`.
+ let metric = self
+ .0
+ .read()
+ .expect("Lock poisoned for timespan metric on test_get_value.");
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &metric.meta().send_in_pings[0]);
+ metric.test_get_value(glean, queried_ping_name)
+ })
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ let metric = self
+ .0
+ .read()
+ .expect("Lock poisoned for timespan metric on test_get_value.");
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, metric.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::{thread, time::Duration};
+
+ use super::*;
+ use crate::common_test::{lock_test, new_glean};
+ use crate::CommonMetricData;
+
+ #[test]
+ fn timespan_convenient_api() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ let metric: TimespanMetric = TimespanMetric::new(
+ CommonMetricData {
+ name: "timespan".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ },
+ TimeUnit::Millisecond,
+ );
+
+ // Canceling doesn't store data.
+ metric.start();
+ metric.cancel();
+ assert!(metric.test_get_value(None).is_none());
+
+ // Starting and stopping measures time.
+ metric.start();
+ thread::sleep(Duration::from_millis(10));
+ metric.stop();
+ assert!(10 <= metric.test_get_value(None).unwrap());
+
+ // No errors
+ assert_eq!(
+ metric.test_get_num_recorded_errors(ErrorType::InvalidState, None),
+ 0
+ );
+
+ // Stopping without starting is an error
+ metric.stop();
+ assert_eq!(
+ metric.test_get_num_recorded_errors(ErrorType::InvalidState, None),
+ 1
+ )
+ }
+}
diff --git a/third_party/rust/glean/src/private/timing_distribution.rs b/third_party/rust/glean/src/private/timing_distribution.rs
new file mode 100644
index 0000000000..5e1a9f930f
--- /dev/null
+++ b/third_party/rust/glean/src/private/timing_distribution.rs
@@ -0,0 +1,99 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::{Arc, RwLock};
+
+use glean_core::metrics::{DistributionData, MetricType, TimeUnit, TimerId};
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer-facing API for recording timing distribution metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct TimingDistributionMetric(
+ pub(crate) Arc<RwLock<glean_core::metrics::TimingDistributionMetric>>,
+);
+
+impl TimingDistributionMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self {
+ Self(Arc::new(RwLock::new(
+ glean_core::metrics::TimingDistributionMetric::new(meta, time_unit),
+ )))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::TimingDistribution for TimingDistributionMetric {
+ fn start(&self) -> TimerId {
+ let start_time = time::precise_time_ns();
+ self.0.write().unwrap().set_start(start_time)
+ }
+
+ fn stop_and_accumulate(&self, id: TimerId) {
+ let stop_time = time::precise_time_ns();
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || {
+ crate::with_glean(|glean| {
+ metric
+ .write()
+ .unwrap()
+ .set_stop_and_accumulate(glean, id, stop_time)
+ })
+ });
+ }
+
+ fn cancel(&self, id: TimerId) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || metric.write().unwrap().cancel(id));
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ ping_name: S,
+ ) -> Option<DistributionData> {
+ crate::block_on_dispatcher();
+
+ crate::with_glean(|glean| {
+ // The order of taking these locks matter. Glean must be first.
+ let inner = self
+ .0
+ .read()
+ .expect("Lock poisoned for timing distribution metric on test_get_value.");
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &inner.meta().send_in_pings[0]);
+
+ inner.test_get_value(glean, queried_ping_name)
+ })
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(
+ &glean,
+ self.0.read().unwrap().meta(),
+ error,
+ ping_name.into(),
+ )
+ .unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean/src/private/uuid.rs b/third_party/rust/glean/src/private/uuid.rs
new file mode 100644
index 0000000000..fc82b67b8c
--- /dev/null
+++ b/third_party/rust/glean/src/private/uuid.rs
@@ -0,0 +1,69 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type, otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer facing API for recording UUID metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct UuidMetric(pub(crate) Arc<glean_core::metrics::UuidMetric>);
+
+impl UuidMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ Self(Arc::new(glean_core::metrics::UuidMetric::new(meta)))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::Uuid for UuidMetric {
+ fn set(&self, value: uuid::Uuid) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
+ }
+
+ fn generate_and_set(&self) -> uuid::Uuid {
+ // TODO: We can use glean-core's generate_and_set after bug 1673017.
+ let uuid = uuid::Uuid::new_v4();
+ self.set(uuid);
+ uuid
+ }
+
+ fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<uuid::Uuid> {
+ crate::block_on_dispatcher();
+
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.0.meta().send_in_pings[0]);
+
+ crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
+ }
+
+ fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
+ &self,
+ error: ErrorType,
+ ping_name: S,
+ ) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::with_glean_mut(|glean| {
+ glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
+ .unwrap_or(0)
+ })
+ }
+}