nimbus/
metrics.rs
1use 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}