diff options
Diffstat (limited to 'third_party/rust/glean/src/private/object.rs')
-rw-r--r-- | third_party/rust/glean/src/private/object.rs | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/third_party/rust/glean/src/private/object.rs b/third_party/rust/glean/src/private/object.rs new file mode 100644 index 0000000000..f7403ec889 --- /dev/null +++ b/third_party/rust/glean/src/private/object.rs @@ -0,0 +1,192 @@ +// 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::marker::PhantomData; + +use glean_core::metrics::JsonValue; +use glean_core::traits; + +use crate::ErrorType; + +// 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. + +/// Developer-facing API for recording object 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 ObjectMetric<K> { + pub(crate) inner: glean_core::metrics::ObjectMetric, + object_type: PhantomData<K>, +} + +impl<K: traits::ObjectSerialize> ObjectMetric<K> { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + let inner = glean_core::metrics::ObjectMetric::new(meta); + Self { + inner, + object_type: PhantomData, + } + } + + /// Sets to the specified structure. + /// + /// # Arguments + /// + /// * `object` - the object to set. + pub fn set(&self, object: K) { + let obj = object + .into_serialized_object() + .expect("failed to serialize object. This should be impossible."); + self.inner.set(obj); + } + + /// Sets to the specified structure. + /// + /// Parses the passed JSON string. + /// If it can't be parsed into a valid object it records an invalid value error. + /// + /// # Arguments + /// + /// * `object` - JSON representation of the object to set. + pub fn set_string(&self, object: String) { + let data = match K::from_str(&object) { + Ok(data) => data, + Err(_) => { + self.inner.record_schema_error(); + return; + } + }; + self.set(data) + } + + /// **Test-only API (exported for FFI purposes).** + /// + /// Gets the currently stored value as JSON-encoded string. + /// + /// This doesn't clear the stored value. + pub fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<JsonValue> { + let ping_name = ping_name.into().map(|s| s.to_string()); + self.inner.test_get_value(ping_name) + } + + /// **Exported for test purposes.** + /// + /// Gets the number of recorded errors for the given metric and error type. + /// + /// # Arguments + /// + /// * `error` - The type of error + /// + /// # Returns + /// + /// The number of errors reported. + pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 { + self.inner.test_get_num_recorded_errors(error) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::common_test::{lock_test, new_glean}; + use crate::CommonMetricData; + + use serde_json::json; + + #[test] + fn simple_array() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + type SimpleArray = Vec<i64>; + + let metric: ObjectMetric<SimpleArray> = ObjectMetric::new(CommonMetricData { + name: "object".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }); + + let arr = SimpleArray::from([1, 2, 3]); + metric.set(arr); + + let data = metric.test_get_value(None).expect("no object recorded"); + let expected = json!([1, 2, 3]); + assert_eq!(expected, data); + } + + #[test] + fn complex_nested_object() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + type BalloonsObject = Vec<BalloonsObjectItem>; + + #[derive( + Debug, Hash, Eq, PartialEq, traits::__serde::Deserialize, traits::__serde::Serialize, + )] + #[serde(crate = "traits::__serde")] + #[serde(deny_unknown_fields)] + struct BalloonsObjectItem { + #[serde(skip_serializing_if = "Option::is_none")] + colour: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + diameter: Option<i64>, + } + + let metric: ObjectMetric<BalloonsObject> = ObjectMetric::new(CommonMetricData { + name: "object".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }); + + let balloons = BalloonsObject::from([ + BalloonsObjectItem { + colour: Some("red".to_string()), + diameter: Some(5), + }, + BalloonsObjectItem { + colour: Some("green".to_string()), + diameter: None, + }, + ]); + metric.set(balloons); + + let data = metric.test_get_value(None).expect("no object recorded"); + let expected = json!([ + { "colour": "red", "diameter": 5 }, + { "colour": "green" }, + ]); + assert_eq!(expected, data); + } + + #[test] + fn set_string_api() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + type SimpleArray = Vec<i64>; + + let metric: ObjectMetric<SimpleArray> = ObjectMetric::new(CommonMetricData { + name: "object".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }); + + let arr_str = String::from("[1, 2, 3]"); + metric.set_string(arr_str); + + let data = metric.test_get_value(None).expect("no object recorded"); + let expected = json!([1, 2, 3]); + assert_eq!(expected, data); + } +} |