1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::VecDeque;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ActivityLogEntry {
10 pub timestamp: DateTime<Utc>,
12 pub activity_type: ActivityType,
14 pub step_id: Option<String>,
16 pub message: String,
18 pub context: serde_json::Value,
20}
21
22#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
24pub enum ActivityType {
25 #[serde(rename = "workflow_started")]
27 WorkflowStarted,
28 #[serde(rename = "workflow_completed")]
30 WorkflowCompleted,
31 #[serde(rename = "workflow_failed")]
33 WorkflowFailed,
34 #[serde(rename = "workflow_paused")]
36 WorkflowPaused,
37 #[serde(rename = "workflow_resumed")]
39 WorkflowResumed,
40 #[serde(rename = "workflow_cancelled")]
42 WorkflowCancelled,
43 #[serde(rename = "step_started")]
45 StepStarted,
46 #[serde(rename = "step_completed")]
48 StepCompleted,
49 #[serde(rename = "step_failed")]
51 StepFailed,
52 #[serde(rename = "step_skipped")]
54 StepSkipped,
55 #[serde(rename = "approval_requested")]
57 ApprovalRequested,
58 #[serde(rename = "approval_granted")]
60 ApprovalGranted,
61 #[serde(rename = "approval_denied")]
63 ApprovalDenied,
64 #[serde(rename = "state_transition")]
66 StateTransition,
67 #[serde(rename = "error")]
69 Error,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ActivityLogger {
75 entries: VecDeque<ActivityLogEntry>,
77 max_entries: usize,
79}
80
81impl ActivityLogger {
82 pub fn new(max_entries: usize) -> Self {
84 ActivityLogger {
85 entries: VecDeque::new(),
86 max_entries,
87 }
88 }
89
90 pub fn log(
92 &mut self,
93 activity_type: ActivityType,
94 step_id: Option<String>,
95 message: String,
96 context: serde_json::Value,
97 ) {
98 let entry = ActivityLogEntry {
99 timestamp: Utc::now(),
100 activity_type,
101 step_id,
102 message,
103 context,
104 };
105
106 self.entries.push_back(entry);
107
108 if self.entries.len() > self.max_entries {
110 self.entries.pop_front();
111 }
112 }
113
114 pub fn log_workflow_started(&mut self, workflow_id: &str) {
116 self.log(
117 ActivityType::WorkflowStarted,
118 None,
119 format!("Workflow '{}' started", workflow_id),
120 serde_json::json!({"workflow_id": workflow_id}),
121 );
122 }
123
124 pub fn log_workflow_completed(&mut self, workflow_id: &str, duration_ms: u64) {
126 self.log(
127 ActivityType::WorkflowCompleted,
128 None,
129 format!("Workflow '{}' completed in {}ms", workflow_id, duration_ms),
130 serde_json::json!({"workflow_id": workflow_id, "duration_ms": duration_ms}),
131 );
132 }
133
134 pub fn log_workflow_failed(&mut self, workflow_id: &str, error: &str) {
136 self.log(
137 ActivityType::WorkflowFailed,
138 None,
139 format!("Workflow '{}' failed: {}", workflow_id, error),
140 serde_json::json!({"workflow_id": workflow_id, "error": error}),
141 );
142 }
143
144 pub fn log_workflow_paused(&mut self, workflow_id: &str) {
146 self.log(
147 ActivityType::WorkflowPaused,
148 None,
149 format!("Workflow '{}' paused", workflow_id),
150 serde_json::json!({"workflow_id": workflow_id}),
151 );
152 }
153
154 pub fn log_workflow_resumed(&mut self, workflow_id: &str) {
156 self.log(
157 ActivityType::WorkflowResumed,
158 None,
159 format!("Workflow '{}' resumed", workflow_id),
160 serde_json::json!({"workflow_id": workflow_id}),
161 );
162 }
163
164 pub fn log_workflow_cancelled(&mut self, workflow_id: &str) {
166 self.log(
167 ActivityType::WorkflowCancelled,
168 None,
169 format!("Workflow '{}' cancelled", workflow_id),
170 serde_json::json!({"workflow_id": workflow_id}),
171 );
172 }
173
174 pub fn log_step_started(&mut self, step_id: &str, step_name: &str) {
176 self.log(
177 ActivityType::StepStarted,
178 Some(step_id.to_string()),
179 format!("Step '{}' started", step_name),
180 serde_json::json!({"step_id": step_id, "step_name": step_name}),
181 );
182 }
183
184 pub fn log_step_completed(&mut self, step_id: &str, step_name: &str, duration_ms: u64) {
186 self.log(
187 ActivityType::StepCompleted,
188 Some(step_id.to_string()),
189 format!("Step '{}' completed in {}ms", step_name, duration_ms),
190 serde_json::json!({"step_id": step_id, "step_name": step_name, "duration_ms": duration_ms}),
191 );
192 }
193
194 pub fn log_step_failed(&mut self, step_id: &str, step_name: &str, error: &str) {
196 self.log(
197 ActivityType::StepFailed,
198 Some(step_id.to_string()),
199 format!("Step '{}' failed: {}", step_name, error),
200 serde_json::json!({"step_id": step_id, "step_name": step_name, "error": error}),
201 );
202 }
203
204 pub fn log_step_skipped(&mut self, step_id: &str, step_name: &str) {
206 self.log(
207 ActivityType::StepSkipped,
208 Some(step_id.to_string()),
209 format!("Step '{}' skipped", step_name),
210 serde_json::json!({"step_id": step_id, "step_name": step_name}),
211 );
212 }
213
214 pub fn log_approval_requested(&mut self, step_id: &str, message: &str) {
216 self.log(
217 ActivityType::ApprovalRequested,
218 Some(step_id.to_string()),
219 format!("Approval requested: {}", message),
220 serde_json::json!({"step_id": step_id, "message": message}),
221 );
222 }
223
224 pub fn log_approval_granted(&mut self, step_id: &str) {
226 self.log(
227 ActivityType::ApprovalGranted,
228 Some(step_id.to_string()),
229 "Approval granted".to_string(),
230 serde_json::json!({"step_id": step_id}),
231 );
232 }
233
234 pub fn log_approval_denied(&mut self, step_id: &str) {
236 self.log(
237 ActivityType::ApprovalDenied,
238 Some(step_id.to_string()),
239 "Approval denied".to_string(),
240 serde_json::json!({"step_id": step_id}),
241 );
242 }
243
244 pub fn log_state_transition(&mut self, from_state: &str, to_state: &str) {
246 self.log(
247 ActivityType::StateTransition,
248 None,
249 format!("State transition: {} -> {}", from_state, to_state),
250 serde_json::json!({"from_state": from_state, "to_state": to_state}),
251 );
252 }
253
254 pub fn log_error(&mut self, step_id: Option<&str>, error: &str) {
256 self.log(
257 ActivityType::Error,
258 step_id.map(|s| s.to_string()),
259 format!("Error: {}", error),
260 serde_json::json!({"error": error}),
261 );
262 }
263
264 pub fn get_entries(&self) -> Vec<ActivityLogEntry> {
266 self.entries.iter().cloned().collect()
267 }
268
269 pub fn get_entries_by_type(&self, activity_type: ActivityType) -> Vec<ActivityLogEntry> {
271 self.entries
272 .iter()
273 .filter(|entry| entry.activity_type == activity_type)
274 .cloned()
275 .collect()
276 }
277
278 pub fn get_entries_for_step(&self, step_id: &str) -> Vec<ActivityLogEntry> {
280 self.entries
281 .iter()
282 .filter(|entry| entry.step_id.as_deref() == Some(step_id))
283 .cloned()
284 .collect()
285 }
286
287 pub fn clear(&mut self) {
289 self.entries.clear();
290 }
291
292 pub fn len(&self) -> usize {
294 self.entries.len()
295 }
296
297 pub fn is_empty(&self) -> bool {
299 self.entries.is_empty()
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn test_create_activity_logger() {
309 let logger = ActivityLogger::new(100);
310 assert!(logger.is_empty());
311 assert_eq!(logger.len(), 0);
312 }
313
314 #[test]
315 fn test_log_activity() {
316 let mut logger = ActivityLogger::new(100);
317
318 logger.log(
319 ActivityType::WorkflowStarted,
320 None,
321 "Workflow started".to_string(),
322 serde_json::json!({}),
323 );
324
325 assert_eq!(logger.len(), 1);
326 assert!(!logger.is_empty());
327 }
328
329 #[test]
330 fn test_log_workflow_started() {
331 let mut logger = ActivityLogger::new(100);
332 logger.log_workflow_started("test-workflow");
333
334 assert_eq!(logger.len(), 1);
335 let entries = logger.get_entries();
336 assert_eq!(entries[0].activity_type, ActivityType::WorkflowStarted);
337 }
338
339 #[test]
340 fn test_log_step_started() {
341 let mut logger = ActivityLogger::new(100);
342 logger.log_step_started("step1", "Step 1");
343
344 assert_eq!(logger.len(), 1);
345 let entries = logger.get_entries();
346 assert_eq!(entries[0].activity_type, ActivityType::StepStarted);
347 assert_eq!(entries[0].step_id, Some("step1".to_string()));
348 }
349
350 #[test]
351 fn test_get_entries_by_type() {
352 let mut logger = ActivityLogger::new(100);
353 logger.log_workflow_started("test-workflow");
354 logger.log_step_started("step1", "Step 1");
355 logger.log_workflow_completed("test-workflow", 100);
356
357 let workflow_entries = logger.get_entries_by_type(ActivityType::WorkflowStarted);
358 assert_eq!(workflow_entries.len(), 1);
359
360 let step_entries = logger.get_entries_by_type(ActivityType::StepStarted);
361 assert_eq!(step_entries.len(), 1);
362 }
363
364 #[test]
365 fn test_get_entries_for_step() {
366 let mut logger = ActivityLogger::new(100);
367 logger.log_step_started("step1", "Step 1");
368 logger.log_step_completed("step1", "Step 1", 100);
369 logger.log_step_started("step2", "Step 2");
370
371 let step1_entries = logger.get_entries_for_step("step1");
372 assert_eq!(step1_entries.len(), 2);
373
374 let step2_entries = logger.get_entries_for_step("step2");
375 assert_eq!(step2_entries.len(), 1);
376 }
377
378 #[test]
379 fn test_max_entries_limit() {
380 let mut logger = ActivityLogger::new(3);
381
382 logger.log_workflow_started("workflow1");
383 logger.log_workflow_started("workflow2");
384 logger.log_workflow_started("workflow3");
385 logger.log_workflow_started("workflow4");
386
387 assert_eq!(logger.len(), 3);
389 }
390
391 #[test]
392 fn test_clear_entries() {
393 let mut logger = ActivityLogger::new(100);
394 logger.log_workflow_started("test-workflow");
395 logger.log_step_started("step1", "Step 1");
396
397 assert_eq!(logger.len(), 2);
398
399 logger.clear();
400 assert!(logger.is_empty());
401 assert_eq!(logger.len(), 0);
402 }
403
404 #[test]
405 fn test_log_error() {
406 let mut logger = ActivityLogger::new(100);
407 logger.log_error(Some("step1"), "Something went wrong");
408
409 assert_eq!(logger.len(), 1);
410 let entries = logger.get_entries();
411 assert_eq!(entries[0].activity_type, ActivityType::Error);
412 assert_eq!(entries[0].step_id, Some("step1".to_string()));
413 }
414
415 #[test]
416 fn test_log_approval_workflow() {
417 let mut logger = ActivityLogger::new(100);
418 logger.log_approval_requested("step1", "Please review");
419 logger.log_approval_granted("step1");
420
421 assert_eq!(logger.len(), 2);
422
423 let approval_entries = logger.get_entries_by_type(ActivityType::ApprovalRequested);
424 assert_eq!(approval_entries.len(), 1);
425
426 let granted_entries = logger.get_entries_by_type(ActivityType::ApprovalGranted);
427 assert_eq!(granted_entries.len(), 1);
428 }
429}