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
5#[cfg(feature = "stateful")]
6use crate::enrollment::PreviousGeckoPrefState;
7
8use crate::{enrollment::ExperimentEnrollment, EnrolledFeature, EnrollmentStatus};
9use serde_derive::{Deserialize, Serialize};
10
11pub trait MetricsHandler: Send + Sync {
12    fn record_enrollment_statuses(&self, enrollment_status_extras: Vec<EnrollmentStatusExtraDef>);
13
14    #[cfg(feature = "stateful")]
15    fn record_feature_activation(&self, event: FeatureExposureExtraDef);
16
17    #[cfg(feature = "stateful")]
18    fn record_feature_exposure(&self, event: FeatureExposureExtraDef);
19
20    #[cfg(feature = "stateful")]
21    fn record_malformed_feature_config(&self, event: MalformedFeatureConfigExtraDef);
22}
23
24#[derive(Serialize, Deserialize, Clone)]
25pub struct EnrollmentStatusExtraDef {
26    pub branch: Option<String>,
27    pub conflict_slug: Option<String>,
28    pub error_string: Option<String>,
29    pub reason: Option<String>,
30    pub slug: Option<String>,
31    pub status: Option<String>,
32    #[cfg(not(feature = "stateful"))]
33    pub user_id: Option<String>,
34    #[cfg(feature = "stateful")]
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub prev_gecko_pref_states: Option<Vec<PreviousGeckoPrefState>>,
37}
38
39#[cfg(test)]
40impl EnrollmentStatusExtraDef {
41    pub fn branch(&self) -> &str {
42        self.branch.as_ref().unwrap()
43    }
44
45    pub fn conflict_slug(&self) -> &str {
46        self.conflict_slug.as_ref().unwrap()
47    }
48
49    pub fn error_string(&self) -> &str {
50        self.error_string.as_ref().unwrap()
51    }
52
53    pub fn reason(&self) -> &str {
54        self.reason.as_ref().unwrap()
55    }
56
57    pub fn slug(&self) -> &str {
58        self.slug.as_ref().unwrap()
59    }
60
61    pub fn status(&self) -> &str {
62        self.status.as_ref().unwrap()
63    }
64
65    #[cfg(not(feature = "stateful"))]
66    pub fn user_id(&self) -> &str {
67        self.user_id.as_ref().unwrap()
68    }
69}
70
71impl From<ExperimentEnrollment> for EnrollmentStatusExtraDef {
72    fn from(enrollment: ExperimentEnrollment) -> Self {
73        let mut branch_value: Option<String> = None;
74        let mut reason_value: Option<String> = None;
75        let mut error_value: Option<String> = None;
76        match &enrollment.status {
77            EnrollmentStatus::Enrolled { reason, branch, .. } => {
78                branch_value = Some(branch.to_owned());
79                reason_value = Some(reason.to_string());
80            }
81            EnrollmentStatus::Disqualified { reason, branch, .. } => {
82                branch_value = Some(branch.to_owned());
83                reason_value = Some(reason.to_string());
84            }
85            EnrollmentStatus::NotEnrolled { reason } => {
86                reason_value = Some(reason.to_string());
87            }
88            EnrollmentStatus::WasEnrolled { branch, .. } => branch_value = Some(branch.to_owned()),
89            EnrollmentStatus::Error { reason } => {
90                error_value = Some(reason.to_owned());
91            }
92        }
93        EnrollmentStatusExtraDef {
94            branch: branch_value,
95            conflict_slug: None,
96            error_string: error_value,
97            reason: reason_value,
98            slug: Some(enrollment.slug),
99            status: Some(enrollment.status.name()),
100            #[cfg(not(feature = "stateful"))]
101            user_id: None,
102            #[cfg(feature = "stateful")]
103            prev_gecko_pref_states: None,
104        }
105    }
106}
107
108#[derive(Clone)]
109pub struct FeatureExposureExtraDef {
110    pub branch: Option<String>,
111    pub slug: String,
112    pub feature_id: String,
113}
114
115impl From<EnrolledFeature> for FeatureExposureExtraDef {
116    fn from(value: EnrolledFeature) -> Self {
117        Self {
118            feature_id: value.feature_id,
119            branch: value.branch,
120            slug: value.slug,
121        }
122    }
123}
124
125#[derive(Clone, Debug, Default, PartialEq)]
126pub struct MalformedFeatureConfigExtraDef {
127    pub slug: Option<String>,
128    pub branch: Option<String>,
129    pub feature_id: String,
130    pub part: String,
131}
132
133#[cfg(feature = "stateful")]
134impl MalformedFeatureConfigExtraDef {
135    pub(crate) fn from(value: EnrolledFeature, part: String) -> Self {
136        Self {
137            slug: Some(value.slug),
138            branch: value.branch,
139            feature_id: value.feature_id,
140            part,
141        }
142    }
143
144    pub(crate) fn new(feature_id: String, part: String) -> Self {
145        Self {
146            feature_id,
147            part,
148            ..Default::default()
149        }
150    }
151}