statsig_rust/event_logging/event_queue/
queued_gate_expo.rs1use std::borrow::Cow;
2
3use crate::{
4 evaluation::evaluation_types::{ExtraExposureInfo, GateEvaluation},
5 event_logging::{
6 event_logger::ExposureTrigger,
7 exposure_sampling::{EvtSamplingDecision, ExposureSamplingKey},
8 exposure_utils::{get_metadata_with_details, get_statsig_metadata_with_sampling_decision},
9 statsig_event::StatsigEvent,
10 statsig_event_internal::{StatsigEventInternal, GATE_EXPOSURE_EVENT_NAME},
11 },
12 interned_string::InternedString,
13 user::{StatsigUserInternal, StatsigUserLoggable},
14 EvaluationDetails, SecondaryExposure,
15};
16
17use super::queued_event::{EnqueueOperation, QueuedEvent, QueuedExposure};
18use crate::event_logging::statsig_event::string_metadata_to_value_metadata;
19
20pub struct EnqueueGateExpoOp<'a> {
21 pub user: &'a StatsigUserInternal<'a, 'a>,
22 pub queried_gate_name: &'a str,
23 pub evaluation: Option<Cow<'a, GateEvaluation>>,
24 pub details: EvaluationDetails,
25 pub trigger: ExposureTrigger,
26 pub exposure_time: u64,
27}
28
29impl EnqueueOperation for EnqueueGateExpoOp<'_> {
30 fn as_exposure(&self) -> Option<&impl QueuedExposure<'_>> {
31 Some(self)
32 }
33
34 fn into_queued_event(self, sampling_decision: EvtSamplingDecision) -> QueuedEvent {
35 let (
36 gate_name, rule_id,
38 value,
39 version,
40 override_config_name,
41 secondary_exposures,
42 ) = extract_from_cow(self.evaluation);
43
44 QueuedEvent::GateExposure(QueuedGateExposureEvent {
45 user: self.user.to_loggable(),
46 gate_name,
47 value,
48 exposure_time: self.exposure_time,
49 version,
50 rule_id,
51 secondary_exposures,
52 evaluation_details: self.details,
53 override_config_name,
54 exposure_trigger: self.trigger,
55 sampling_decision,
56 })
57 }
58}
59
60impl<'a> QueuedExposure<'a> for EnqueueGateExpoOp<'a> {
61 fn create_exposure_sampling_key(&self) -> ExposureSamplingKey {
62 let user_data = &self.user.user_ref.data;
63 let evaluation = self.evaluation.as_ref().map(|e| &e.base);
64 let unit_id_type = self
65 .evaluation
66 .as_ref()
67 .and_then(|eval| eval.id_type.as_ref())
68 .map(|id| id.as_str());
69 let value = self.evaluation.as_ref().is_some_and(|e| e.value);
70 let additional_hash = value as u64;
71
72 ExposureSamplingKey::new(evaluation, user_data, additional_hash, unit_id_type)
73 }
74
75 fn get_rule_id_ref(&'a self) -> &'a str {
76 rule_id_ref(&self.evaluation)
77 }
78
79 fn get_extra_exposure_info_ref(&'a self) -> Option<&'a ExtraExposureInfo> {
80 self.evaluation.as_ref()?.base.exposure_info.as_ref()
81 }
82}
83
84pub struct QueuedGateExposureEvent {
85 pub user: StatsigUserLoggable,
86 pub gate_name: InternedString,
87 pub value: bool,
88 pub rule_id: InternedString,
89 pub secondary_exposures: Option<Vec<SecondaryExposure>>,
90 pub evaluation_details: EvaluationDetails,
91 pub version: Option<u32>,
92 pub exposure_trigger: ExposureTrigger,
93 pub sampling_decision: EvtSamplingDecision,
94 pub override_config_name: Option<InternedString>,
95 pub exposure_time: u64,
96}
97
98impl QueuedGateExposureEvent {
99 pub fn into_statsig_event_internal(self) -> StatsigEventInternal {
100 let mut metadata = get_metadata_with_details(self.evaluation_details);
101 metadata.insert("gate".into(), self.gate_name.unperformant_to_string());
102 metadata.insert("gateValue".into(), self.value.to_string());
103
104 metadata.insert("ruleID".into(), self.rule_id.unperformant_to_string());
107
108 if self.exposure_trigger == ExposureTrigger::Manual {
109 metadata.insert("isManualExposure".into(), "true".into());
110 }
111
112 if let Some(version) = self.version {
113 metadata.insert("configVersion".into(), version.to_string());
114 }
115
116 if let Some(override_config_name) = self.override_config_name {
117 metadata.insert(
118 "overrideConfigName".into(),
119 override_config_name.unperformant_to_string(),
120 );
121 }
122
123 let statsig_metadata = get_statsig_metadata_with_sampling_decision(self.sampling_decision);
124
125 let event = StatsigEvent {
126 event_name: GATE_EXPOSURE_EVENT_NAME.into(),
127 value: None,
128 metadata: Some(string_metadata_to_value_metadata(metadata)),
129 statsig_metadata: Some(statsig_metadata),
130 };
131
132 StatsigEventInternal::new(
133 self.exposure_time,
134 self.user,
135 event,
136 self.secondary_exposures,
137 )
138 }
139}
140
141type ExtractInfoResult = (
142 InternedString,
143 InternedString,
144 bool,
145 Option<u32>,
146 Option<InternedString>,
147 Option<Vec<SecondaryExposure>>,
148);
149
150fn extract_from_cow(moo: Option<Cow<'_, GateEvaluation>>) -> ExtractInfoResult {
151 let moo = match moo {
152 Some(m) => m,
153 None => {
154 return (
155 InternedString::default(),
156 InternedString::default(),
157 false,
158 None,
159 None,
160 None,
161 )
162 }
163 };
164
165 match moo {
166 Cow::Borrowed(evaluation) => {
167 let name = evaluation.base.name.clone();
168 let rule_id = evaluation.base.rule_id.clone();
169 let value = evaluation.value;
170 let expo_info = evaluation.base.exposure_info.clone();
171 let secondary_exposures = evaluation.base.secondary_exposures.clone();
172
173 let version = expo_info.as_ref().and_then(|info| info.version);
174 let override_config_name = expo_info
175 .as_ref()
176 .and_then(|info| info.override_config_name.clone());
177
178 (
179 name,
180 rule_id,
181 value,
182 version,
183 override_config_name,
184 Some(secondary_exposures),
185 )
186 }
187 Cow::Owned(evaluation) => {
188 let name = evaluation.base.name;
189 let rule_id = evaluation.base.rule_id;
190 let value = evaluation.value;
191 let expo_info = evaluation.base.exposure_info;
192 let secondary_exposures = evaluation.base.secondary_exposures;
193
194 let version = expo_info.as_ref().and_then(|info| info.version);
195 let override_config_name = expo_info.and_then(|info| info.override_config_name.clone());
196
197 (
198 name,
199 rule_id,
200 value,
201 version,
202 override_config_name,
203 Some(secondary_exposures),
204 )
205 }
206 }
207}
208
209fn rule_id_ref<'a>(moo: &'a Option<Cow<'a, GateEvaluation>>) -> &'a str {
210 moo.as_ref().map_or("", |x| x.base.rule_id.as_str())
211}