Skip to main content

statsig_rust/sdk_diagnostics/
marker.rs

1use 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    // TODO add more as needed
195}
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}