statsig_rust/sdk_diagnostics/
marker.rs1use chrono::Utc;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5use crate::evaluation::evaluation_details::EvaluationDetails;
6
7pub const TRUE_STR: &str = "true";
8pub const FALSE_STR: &str = "false";
9
10#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
11pub enum KeyType {
12 #[serde(rename = "initialize")]
13 Initialize,
14 #[serde(rename = "overall")]
15 Overall,
16 #[serde(rename = "download_config_specs")]
17 DownloadConfigSpecs,
18 #[serde(rename = "get_id_list")]
19 GetIDList,
20 #[serde(rename = "get_id_list_sources")]
21 GetIDListSources,
22}
23
24#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
25pub enum StepType {
26 #[serde(rename = "process")]
27 Process,
28 #[serde(rename = "network_request")]
29 NetworkRequest,
30}
31
32#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
33pub enum ActionType {
34 #[serde(rename = "start")]
35 Start,
36 #[serde(rename = "end")]
37 End,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41#[serde(rename_all = "camelCase")]
42pub struct Marker {
43 pub key: KeyType,
44
45 action: ActionType,
46
47 timestamp: u64,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
50 attempt: Option<u32>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
53 config_name: Option<String>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
56 error: Option<HashMap<String, String>>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
59 id_list_count: Option<u32>,
60
61 #[serde(rename = "markerID")]
62 #[serde(skip_serializing_if = "Option::is_none")]
63 marker_id: Option<String>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
66 message: Option<String>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
69 sdk_region: Option<String>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
72 status_code: Option<u16>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
75 step: Option<StepType>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
78 success: Option<bool>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
81 url: Option<String>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
84 evaluation_details: Option<EvaluationDetails>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
87 config_spec_ready: Option<bool>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
90 source: Option<String>,
91
92 #[serde(skip_serializing_if = "Option::is_none")]
93 content_type: Option<String>,
94}
95
96impl Marker {
97 #[must_use]
98 pub fn new(key: KeyType, action: ActionType, step: Option<StepType>) -> Self {
99 Self {
100 key,
101 action,
102 step,
103 success: None,
104 timestamp: Utc::now().timestamp_millis() as u64,
105 status_code: None,
106 url: None,
107 id_list_count: None,
108 sdk_region: None,
109 marker_id: None,
110 attempt: None,
111 config_name: None,
112 message: None,
113 error: None,
114 evaluation_details: None,
115 config_spec_ready: None,
116 source: None,
117 content_type: None,
118 }
119 }
120
121 #[must_use]
122 pub fn with_is_success(mut self, success: bool) -> Self {
123 self.success = Some(success);
124 self
125 }
126
127 #[must_use]
128 pub fn with_status_code(mut self, status_code: u16) -> Self {
129 self.status_code = Some(status_code);
130 self
131 }
132
133 #[must_use]
134 pub fn with_attempt(mut self, attempt: u32) -> Self {
135 self.attempt = Some(attempt);
136 self
137 }
138
139 #[must_use]
140 pub fn with_url(mut self, url: String) -> Self {
141 self.url = Some(url);
142 self
143 }
144
145 #[must_use]
146 pub fn with_message(mut self, message: String) -> Self {
147 self.message = Some(message);
148 self
149 }
150
151 #[must_use]
152 pub fn with_eval_details(mut self, details: EvaluationDetails) -> Self {
153 self.evaluation_details = Some(details);
154 self
155 }
156
157 #[must_use]
158 pub fn with_config_spec_ready(mut self, ready: bool) -> Self {
159 self.config_spec_ready = Some(ready);
160 self
161 }
162
163 #[must_use]
164 pub fn with_source(mut self, source: String) -> Self {
165 self.source = Some(source);
166 self
167 }
168
169 #[must_use]
170 pub fn with_sdk_region(mut self, region: Option<String>) -> Self {
171 self.sdk_region = region;
172 self
173 }
174
175 #[must_use]
176 pub fn with_id_list_count(mut self, count: usize) -> Self {
177 if count <= u32::MAX as usize {
178 self.id_list_count = Some(count as u32);
179 }
180 self
181 }
182
183 #[must_use]
184 pub fn with_error(mut self, error: HashMap<String, String>) -> Self {
185 self.error = Some(error);
186 self
187 }
188
189 #[must_use]
190 pub fn with_content_type(mut self, content_type: Option<String>) -> Self {
191 self.content_type = content_type;
192 self
193 }
194 }
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_marker_new() {
203 let timestamp: u64 = Utc::now().timestamp_millis() as u64;
204 let marker = Marker::new(
205 KeyType::Initialize,
206 ActionType::Start,
207 Some(StepType::Process),
208 );
209
210 assert_eq!(marker.key, KeyType::Initialize);
211 assert_eq!(marker.action, ActionType::Start);
212 assert_eq!(marker.step, Some(StepType::Process));
213 assert_eq!(marker.success, None);
214 assert_eq!(marker.status_code, None);
215 assert_eq!(marker.timestamp, timestamp);
216 }
217
218 #[test]
219 fn test_marker_serialization() {
220 let timestamp: u64 = Utc::now().timestamp_millis() as u64;
221 let marker = Marker::new(
222 KeyType::Initialize,
223 ActionType::Start,
224 Some(StepType::NetworkRequest),
225 )
226 .with_is_success(true)
227 .with_status_code(200)
228 .with_attempt(1);
229
230 let serialized = serde_json::to_string(&marker).expect("Failed to serialize Marker");
231 let expected_json = format!(
232 r#"{{"key":"initialize","action":"start","timestamp":{timestamp},"attempt":1,"statusCode":200,"step":"network_request","success":true}}"#
233 );
234 assert_eq!(serialized, expected_json);
235 }
236}