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