// 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/.
#![cfg(feature =
"with_gecko")]
use nsstring::{nsACString, nsCString};
use thin_vec::ThinVec;
use crate::metrics::__glean_metric_maps
as metric_maps;
use crate::private::EventRecordingError;
#[no_mangle]
pub extern "C" fn fog_event_record(
id: u32,
extra_keys: &ThinVec<nsCString>,
extra_values: &ThinVec<nsCString>,
) {
// If no extra keys are passed, we can shortcut here.
if extra_keys.is_empty() {
if id & (
1 <<
crate::factory::DYNAMIC_METRIC_BIT) >
0 {
let map =
crate::factory::__jog_metric_maps::EVENT_MAP
.read()
.expect(
"Read lock for dynamic metric map was poisoned");
match map.get(&id.into()) {
Some(m) => m.record_raw(Default::default()),
None => panic!(
"No (dynamic) metric for event with id {}", id),
}
return;
}
if metric_maps::record_event_by_id(id, Default::default()).is_err() {
panic!(
"No event for id {}", id);
}
return;
}
assert_eq!(
extra_keys.len(),
extra_values.len(),
"Extra keys and values differ in length. ID: {}",
id
);
// Otherwise we need to decode them and pass them along.
let extra = extra_keys
.iter()
.zip(extra_values.iter())
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
if id & (
1 <<
crate::factory::DYNAMIC_METRIC_BIT) >
0 {
let map =
crate::factory::__jog_metric_maps::EVENT_MAP
.read()
.expect(
"Read lock for dynamic metric map was poisoned");
match map.get(&id.into()) {
Some(m) => m.record_raw(extra),
None => panic!(
"No (dynamic) metric for event with id {}", id),
}
}
else {
match metric_maps::record_event_by_id(id, extra) {
Ok(()) => {}
Err(EventRecordingError::InvalidId) => panic!(
"No event for id {}", id),
Err(_) => panic!(
"Unpossible!"),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn fog_event_test_has_value(id: u32, ping_name: &nsACString) -> bool {
let storage =
if ping_name.is_empty() {
None
}
else {
Some(ping_name.to_utf8().into_owned())
};
if id & (
1 <<
crate::factory::DYNAMIC_METRIC_BIT) >
0 {
let map =
crate::factory::__jog_metric_maps::EVENT_MAP
.read()
.expect(
"Read lock for dynamic metric map was poisoned");
match map.get(&id.into()) {
Some(m) => m.test_get_value(storage.as_deref()).is_some(),
None => panic!(
"No (dynamic) metric for event with id {}", id),
}
}
else {
metric_maps::event_test_get_value_wrapper(id, storage).is_some()
}
}
#[no_mangle]
pub extern "C" fn fog_event_test_get_error(id: u32, error_str: &
mut nsACString) -> bool {
let err =
if id & (
1 <<
crate::factory::DYNAMIC_METRIC_BIT) >
0 {
let map =
crate::factory::__jog_metric_maps::EVENT_MAP
.read()
.expect(
"Read lock for dynamic metric map was poisoned");
match map.get(&id.into()) {
Some(m) => test_get_errors!(m),
None => panic!(
"No (dynamic) metric for event with id {}", id),
}
}
else {
metric_maps::event_test_get_error(id)
};
err.map(|err_str| error_str.assign(&err_str)).is_some()
}
/// FFI-compatible representation of recorded event data.
#[repr(C)]
pub struct FfiRecordedEvent {
timestamp: u64,
category: nsCString,
name: nsCString,
/// Array of extra data, keys and values are interleaved.
extras: ThinVec<nsCString>,
}
#[no_mangle]
pub extern "C" fn fog_event_test_get_value(
id: u32,
ping_name: &nsACString,
out_events: &
mut ThinVec<FfiRecordedEvent>,
) {
let storage =
if ping_name.is_empty() {
None
}
else {
Some(ping_name.to_utf8().into_owned())
};
let events =
if id & (
1 <<
crate::factory::DYNAMIC_METRIC_BIT) >
0 {
let map =
crate::factory::__jog_metric_maps::EVENT_MAP
.read()
.expect(
"Read lock for dynamic metric map was poisoned");
let events =
match map.get(&id.into()) {
Some(m) => m.test_get_value(storage.as_deref()),
None =>
return,
};
match events {
Some(events) => events,
None =>
return,
}
}
else {
match metric_maps::event_test_get_value_wrapper(id, storage) {
Some(events) => events,
None =>
return,
}
};
for event
in events {
let extra = event.extra.unwrap_or_default();
let extra_len = extra.len();
let mut extras = ThinVec::with_capacity(extra_len *
2);
for (k, v)
in extra.into_iter() {
extras.push(nsCString::from(k));
extras.push(nsCString::from(v));
}
let event = FfiRecordedEvent {
timestamp: event.timestamp,
category: nsCString::from(event.category),
name: nsCString::from(event.name),
extras,
};
out_events.push(event);
}
}