1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use uuid::Uuid;
7
8pub const SCHEMA_VERSION: &str = "1.0.0";
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Event {
14 pub schema_version: String,
16
17 pub event_id: Uuid,
19
20 pub timestamp: DateTime<Utc>,
22
23 pub service: ServiceInfo,
25
26 pub user_id: String,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub session_id: Option<String>,
32
33 pub environment: Environment,
35
36 pub event: EventData,
38
39 pub metadata: Metadata,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ServiceInfo {
46 pub name: String,
48
49 pub version: String,
51
52 pub language: String,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub language_version: Option<String>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Environment {
63 pub os: String,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub os_version: Option<String>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub arch: Option<String>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub ci: Option<bool>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub shell: Option<String>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct EventData {
86 #[serde(rename = "type")]
88 pub event_type: String,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub category: Option<String>,
93
94 pub data: serde_json::Value,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct Metadata {
101 pub sdk_version: String,
103
104 pub transmission_timestamp: DateTime<Utc>,
106
107 pub batch_size: usize,
109
110 pub retry_count: u32,
112}
113
114#[derive(Debug, Default)]
116pub struct CommandEventBuilder {
117 command: String,
118 subcommand: Option<String>,
119 flags: Vec<String>,
120 success: Option<bool>,
121 duration_ms: Option<u64>,
122 exit_code: Option<i32>,
123}
124
125impl CommandEventBuilder {
126 pub fn new(command: impl Into<String>) -> Self {
128 Self {
129 command: command.into(),
130 ..Default::default()
131 }
132 }
133
134 pub fn subcommand(mut self, subcommand: impl Into<String>) -> Self {
136 self.subcommand = Some(subcommand.into());
137 self
138 }
139
140 pub fn flag(mut self, flag: impl Into<String>) -> Self {
142 self.flags.push(flag.into());
143 self
144 }
145
146 pub fn success(mut self, success: bool) -> Self {
148 self.success = Some(success);
149 self
150 }
151
152 pub fn duration_ms(mut self, duration_ms: u64) -> Self {
154 self.duration_ms = Some(duration_ms);
155 self
156 }
157
158 pub fn exit_code(mut self, exit_code: i32) -> Self {
160 self.exit_code = Some(exit_code);
161 self
162 }
163
164 pub fn build(self) -> serde_json::Value {
166 let mut data = HashMap::new();
167 data.insert("command".to_string(), serde_json::json!(self.command));
168
169 if let Some(subcommand) = self.subcommand {
170 data.insert("subcommand".to_string(), serde_json::json!(subcommand));
171 }
172
173 if !self.flags.is_empty() {
174 data.insert("flags".to_string(), serde_json::json!(self.flags));
175 }
176
177 if let Some(success) = self.success {
178 data.insert("success".to_string(), serde_json::json!(success));
179 }
180
181 if let Some(duration_ms) = self.duration_ms {
182 data.insert("duration_ms".to_string(), serde_json::json!(duration_ms));
183 }
184
185 if let Some(exit_code) = self.exit_code {
186 data.insert("exit_code".to_string(), serde_json::json!(exit_code));
187 }
188
189 serde_json::json!(data)
190 }
191}
192
193#[derive(Debug, Default)]
195pub struct FeatureEventBuilder {
196 feature: String,
197 method: Option<String>,
198 success: Option<bool>,
199 custom_data: HashMap<String, serde_json::Value>,
200}
201
202impl FeatureEventBuilder {
203 pub fn new(feature: impl Into<String>) -> Self {
205 Self {
206 feature: feature.into(),
207 ..Default::default()
208 }
209 }
210
211 pub fn method(mut self, method: impl Into<String>) -> Self {
213 self.method = Some(method.into());
214 self
215 }
216
217 pub fn success(mut self, success: bool) -> Self {
219 self.success = Some(success);
220 self
221 }
222
223 pub fn data(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
225 self.custom_data.insert(key.into(), value);
226 self
227 }
228
229 pub fn build(self) -> serde_json::Value {
231 let mut data = HashMap::new();
232 data.insert("feature".to_string(), serde_json::json!(self.feature));
233
234 if let Some(method) = self.method {
235 data.insert("method".to_string(), serde_json::json!(method));
236 }
237
238 if let Some(success) = self.success {
239 data.insert("success".to_string(), serde_json::json!(success));
240 }
241
242 for (key, value) in self.custom_data {
243 data.insert(key, value);
244 }
245
246 serde_json::json!(data)
247 }
248}
249
250#[derive(Debug, Serialize, Deserialize)]
252pub struct EventBatch {
253 pub events: Vec<Event>,
255}
256
257impl EventBatch {
258 pub fn new(events: Vec<Event>) -> Self {
260 Self { events }
261 }
262
263 pub fn size(&self) -> usize {
265 self.events.len()
266 }
267
268 pub fn is_empty(&self) -> bool {
270 self.events.is_empty()
271 }
272}