nimbus/
metrics.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use serde_derive::{Deserialize, Serialize};
6
7use crate::EnrollmentStatus;
8use crate::enrollment::ExperimentEnrollment;
9#[cfg(feature = "stateful")]
10use crate::enrollment::PreviousGeckoPrefState;
11pub use crate::metrics::detail::*;
12
13use crate::enrollment::NotEnrolledReason;
14#[derive(Serialize, Deserialize, Clone)]
15#[cfg_attr(test, derive(Debug, Default, Eq, PartialEq))]
16pub struct EnrollmentStatusExtraDef {
17    pub branch: Option<String>,
18    pub conflict_slug: Option<String>,
19    pub error_string: Option<String>,
20    pub reason: Option<String>,
21    pub slug: Option<String>,
22    pub status: Option<String>,
23    #[cfg(not(feature = "stateful"))]
24    pub user_id: Option<String>,
25    #[cfg(feature = "stateful")]
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub prev_gecko_pref_states: Option<Vec<PreviousGeckoPrefState>>,
28}
29
30impl From<ExperimentEnrollment> for EnrollmentStatusExtraDef {
31    fn from(enrollment: ExperimentEnrollment) -> Self {
32        let mut branch_value: Option<String> = None;
33        let mut reason_value: Option<String> = None;
34        let mut error_value: Option<String> = None;
35        let mut conflict_slug_value: Option<String> = None;
36        match &enrollment.status {
37            EnrollmentStatus::Enrolled { reason, branch, .. } => {
38                branch_value = Some(branch.to_owned());
39                reason_value = Some(reason.to_string());
40            }
41            EnrollmentStatus::Disqualified { reason, branch, .. } => {
42                branch_value = Some(branch.to_owned());
43                reason_value = Some(reason.to_string());
44            }
45            EnrollmentStatus::NotEnrolled { reason } => {
46                reason_value = Some(reason.to_string());
47                if let NotEnrolledReason::FeatureConflict { conflict_slug } = reason {
48                    conflict_slug_value = conflict_slug.clone();
49                }
50            }
51            EnrollmentStatus::WasEnrolled { branch, .. } => branch_value = Some(branch.to_owned()),
52            EnrollmentStatus::Error { reason } => {
53                error_value = Some(reason.to_owned());
54            }
55        }
56        EnrollmentStatusExtraDef {
57            branch: branch_value,
58            conflict_slug: conflict_slug_value,
59            error_string: error_value,
60            reason: reason_value,
61            slug: Some(enrollment.slug),
62            status: Some(enrollment.status.name()),
63            #[cfg(not(feature = "stateful"))]
64            user_id: None,
65            #[cfg(feature = "stateful")]
66            prev_gecko_pref_states: None,
67        }
68    }
69}
70
71#[cfg(feature = "stateful")]
72mod detail {
73    use crate::EnrolledFeature;
74
75    use super::EnrollmentStatusExtraDef;
76
77    #[uniffi::trait_interface]
78    pub trait MetricsHandler: Send + Sync {
79        fn record_database_load(&self, event: DatabaseLoadExtraDef);
80
81        fn record_database_migration(&self, event: DatabaseMigrationExtraDef);
82
83        fn record_enrollment_statuses(
84            &self,
85            enrollment_status_extras: Vec<EnrollmentStatusExtraDef>,
86        );
87
88        fn record_feature_activation(&self, event: FeatureExposureExtraDef);
89
90        fn record_feature_exposure(&self, event: FeatureExposureExtraDef);
91
92        fn record_malformed_feature_config(&self, event: MalformedFeatureConfigExtraDef);
93
94        fn submit_targeting_context(&self);
95    }
96
97    #[derive(Clone)]
98    pub struct FeatureExposureExtraDef {
99        pub branch: Option<String>,
100        pub slug: String,
101        pub feature_id: String,
102    }
103
104    impl From<EnrolledFeature> for FeatureExposureExtraDef {
105        fn from(value: EnrolledFeature) -> Self {
106            Self {
107                feature_id: value.feature_id,
108                branch: value.branch,
109                slug: value.slug,
110            }
111        }
112    }
113
114    #[derive(Default)]
115    #[cfg_attr(test, derive(Clone, Debug, Eq, PartialEq))]
116    pub struct MalformedFeatureConfigExtraDef {
117        pub slug: Option<String>,
118        pub branch: Option<String>,
119        pub feature_id: String,
120        pub part: String,
121    }
122
123    impl MalformedFeatureConfigExtraDef {
124        pub(crate) fn from_feature_and_part(value: EnrolledFeature, part: String) -> Self {
125            Self {
126                slug: Some(value.slug),
127                branch: value.branch,
128                feature_id: value.feature_id,
129                part,
130            }
131        }
132
133        pub(crate) fn new(feature_id: String, part: String) -> Self {
134            Self {
135                feature_id,
136                part,
137                ..Default::default()
138            }
139        }
140    }
141
142    #[derive(Default)]
143    #[cfg_attr(test, derive(Clone, Debug, Eq, PartialEq))]
144    pub struct DatabaseLoadExtraDef {
145        pub corrupt: Option<bool>,
146        pub error: Option<String>,
147        pub initial_version: Option<u16>,
148        pub migrated_version: Option<u16>,
149        pub migration_error: Option<String>,
150    }
151
152    #[cfg_attr(test, derive(Clone, Debug, Eq, PartialEq))]
153    pub struct DatabaseMigrationExtraDef {
154        pub reason: String,
155        pub from_version: u16,
156        pub to_version: u16,
157        pub error: Option<String>,
158    }
159}
160
161#[cfg(not(feature = "stateful"))]
162mod detail {
163    use super::EnrollmentStatusExtraDef;
164
165    #[uniffi::trait_interface]
166    pub trait MetricsHandler: Send + Sync {
167        fn record_enrollment_statuses_v2(
168            &self,
169            enrollment_status_extras: Vec<EnrollmentStatusExtraDef>,
170            nimbus_user_id: Option<String>,
171        );
172    }
173}