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