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