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