1use std::collections::HashMap;
2
3use serde::Serialize;
4use serde_with::skip_serializing_none;
5
6use crate::evaluation::dynamic_returnable::DynamicReturnable;
7use crate::evaluation::evaluation_details::EvaluationDetails;
8use crate::interned_string::InternedString;
9use crate::specs_response::explicit_params::ExplicitParameters;
10use crate::user::StatsigUserLoggable;
11use crate::SecondaryExposure;
12
13#[derive(Serialize)]
14#[serde(rename_all = "camelCase")]
15pub(crate) struct FeatureGateRaw<'a> {
16 pub name: &'a str,
17
18 pub value: bool,
19
20 #[serde(rename = "ruleID")]
21 pub rule_id: SuffixedRuleId<'a>,
22
23 pub id_type: Option<&'a InternedString>,
24
25 pub details: &'a EvaluationDetails,
26}
27
28impl<'a> FeatureGateRaw<'a> {
29 pub fn empty(name: &'a str, details: &'a EvaluationDetails) -> Self {
30 Self {
31 name,
32 value: false,
33 details,
34 rule_id: SuffixedRuleId {
35 rule_id: InternedString::empty_ref(),
36 rule_id_suffix: None,
37 },
38 id_type: None,
39 }
40 }
41}
42
43#[derive(Serialize)]
44#[serde(rename_all = "camelCase")]
45pub(crate) struct DynamicConfigRaw<'a> {
46 pub name: &'a str,
47
48 pub value: Option<&'a DynamicReturnable>,
49
50 #[serde(rename = "ruleID")]
51 pub rule_id: SuffixedRuleId<'a>,
52
53 pub id_type: Option<&'a InternedString>,
54
55 pub details: &'a EvaluationDetails,
56}
57
58impl<'a> DynamicConfigRaw<'a> {
59 pub fn empty(name: &'a str, details: &'a EvaluationDetails) -> Self {
60 Self {
61 name,
62 value: None,
63 details,
64 rule_id: SuffixedRuleId {
65 rule_id: InternedString::empty_ref(),
66 rule_id_suffix: None,
67 },
68 id_type: None,
69 }
70 }
71}
72
73#[derive(Serialize)]
74#[serde(rename_all = "camelCase")]
75pub(crate) struct ExperimentRaw<'a> {
76 pub name: &'a str,
77
78 pub value: Option<&'a DynamicReturnable>,
79
80 #[serde(rename = "ruleID")]
81 pub rule_id: SuffixedRuleId<'a>,
82
83 pub id_type: Option<&'a InternedString>,
84
85 pub group_name: Option<&'a InternedString>,
86
87 pub is_experiment_active: Option<bool>,
88
89 pub details: &'a EvaluationDetails,
90
91 pub secondary_exposures: Option<&'a Vec<SecondaryExposure>>,
92}
93
94impl<'a> ExperimentRaw<'a> {
95 pub fn empty(name: &'a str, details: &'a EvaluationDetails) -> Self {
96 Self {
97 name,
98 value: None,
99 details,
100 rule_id: SuffixedRuleId {
101 rule_id: InternedString::empty_ref(),
102 rule_id_suffix: None,
103 },
104 id_type: None,
105 group_name: None,
106 is_experiment_active: None,
107 secondary_exposures: None,
108 }
109 }
110}
111
112fn is_interned_string_none_or_empty(value: &Option<&InternedString>) -> bool {
113 match value {
114 Some(value) => value.is_empty(),
115 None => true,
116 }
117}
118
119#[derive(Serialize)]
120#[serde(rename_all = "camelCase")]
121#[skip_serializing_none]
122pub(crate) struct LayerRaw<'a> {
123 pub name: &'a str,
124
125 pub value: Option<&'a DynamicReturnable>,
126
127 #[serde(rename = "ruleID")]
128 pub rule_id: SuffixedRuleId<'a>,
129
130 pub id_type: Option<&'a InternedString>,
131
132 pub group_name: Option<&'a InternedString>,
133
134 pub is_experiment_active: Option<bool>,
135
136 pub details: &'a EvaluationDetails,
137
138 #[serde(skip_serializing_if = "is_interned_string_none_or_empty")]
139 pub allocated_experiment_name: Option<&'a InternedString>,
140
141 pub disable_exposure: bool,
142
143 pub user: StatsigUserLoggable,
144
145 pub secondary_exposures: Option<&'a Vec<SecondaryExposure>>,
146
147 pub undelegated_secondary_exposures: Option<&'a Vec<SecondaryExposure>>,
148
149 pub explicit_parameters: Option<ExplicitParameters>,
150
151 pub parameter_rule_ids: Option<&'a HashMap<InternedString, InternedString>>,
152}
153
154impl<'a> LayerRaw<'a> {
155 pub fn empty(name: &'a str, details: &'a EvaluationDetails) -> Self {
156 Self {
157 name,
158 details,
159 rule_id: SuffixedRuleId {
160 rule_id: InternedString::empty_ref(),
161 rule_id_suffix: None,
162 },
163 id_type: None,
164 group_name: None,
165 is_experiment_active: None,
166 value: None,
167 allocated_experiment_name: None,
168 disable_exposure: false,
169 user: StatsigUserLoggable::null(),
170 secondary_exposures: None,
171 undelegated_secondary_exposures: None,
172 explicit_parameters: None,
173 parameter_rule_ids: None,
174 }
175 }
176}
177
178#[derive(serde::Deserialize)]
179#[serde(rename_all = "camelCase")]
180#[cfg(feature = "ffi-support")]
181pub(crate) struct PartialLayerRaw {
182 pub name: InternedString,
183
184 #[serde(rename = "ruleID")]
185 pub rule_id: Option<InternedString>,
186
187 pub id_type: Option<InternedString>,
188
189 pub group_name: Option<InternedString>,
190
191 pub details: EvaluationDetails,
192
193 pub allocated_experiment_name: Option<InternedString>,
194 pub disable_exposure: bool,
195 pub user: StatsigUserLoggable,
196 pub secondary_exposures: Option<Vec<SecondaryExposure>>,
197 pub undelegated_secondary_exposures: Option<Vec<SecondaryExposure>>,
198 pub explicit_parameters: Option<ExplicitParameters>,
199 pub parameter_rule_ids: Option<HashMap<InternedString, InternedString>>,
200}
201
202pub struct SuffixedRuleId<'a> {
203 pub rule_id: &'a InternedString,
204 pub rule_id_suffix: Option<&'a str>,
205}
206
207impl<'a> std::fmt::Display for SuffixedRuleId<'a> {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 f.write_str(self.rule_id.as_str())?;
210 if let Some(suffix) = self.rule_id_suffix {
211 f.write_str(":")?;
212 f.write_str(suffix)?;
213 }
214 Ok(())
215 }
216}
217
218impl<'a> Serialize for SuffixedRuleId<'a> {
219 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
220 where
221 S: serde::Serializer,
222 {
223 serializer.collect_str(self)
224 }
225}