1use chrono::{DateTime, TimeZone, Utc};
2use std::fs;
3use std::path::Path;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
7pub struct StagedEvent {
8 pub id: String,
9 pub source: String,
10 pub content: String,
11 pub meta: Option<String>,
12 pub timestamp: i64,
13}
14
15impl StagedEvent {
16 pub fn new(source: &str, content: &str, meta: Option<&str>) -> Self {
17 let now = Utc::now().timestamp();
18 let id = Uuid::new_v4().to_string();
19
20 Self {
21 id,
22 source: source.to_string(),
23 content: content.to_string(),
24 meta: meta.map(|s| s.to_string()),
25 timestamp: now,
26 }
27 }
28
29 pub fn timestamp_utc(&self) -> DateTime<Utc> {
31 Utc.timestamp_opt(self.timestamp, 0).unwrap()
32 }
33
34 pub fn save_to_file(&self, staging_dir: &Path) -> Result<(), std::io::Error> {
36 let filename = format!("{}.json", self.id);
37 let path = staging_dir.join(filename);
38
39 fs::create_dir_all(staging_dir)?;
40 let json = serde_json::to_string_pretty(self)?;
41
42 fs::write(path, json)?;
43 Ok(())
44 }
45
46 pub fn load_from_file(id: &str, staging_dir: &Path) -> Result<Self, std::io::Error> {
48 let filename = format!("{}.json", id);
49 let path = staging_dir.join(filename);
50
51 let json = fs::read_to_string(path)?;
52 let event: StagedEvent = serde_json::from_str(&json)?;
53 Ok(event)
54 }
55
56 pub fn from_file(path: &Path) -> Result<Self, std::io::Error> {
58 let json = fs::read_to_string(path)?;
59 let event: StagedEvent = serde_json::from_str(&json)
60 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
61 Ok(event)
62 }
63
64 pub fn load_all(staging_dir: &Path) -> Result<Vec<Self>, std::io::Error> {
66 let mut events = Vec::new();
67
68 if !staging_dir.exists() {
69 return Ok(events);
70 }
71
72 for entry in fs::read_dir(staging_dir)? {
73 let path = entry?.path();
74 if path.extension() == Some(std::ffi::OsStr::new("json")) {
75 match Self::from_file(&path) {
76 Ok(event) => events.push(event),
77 Err(e) => eprintln!("Failed to parse staging event {}: {}", path.display(), e),
78 }
79 }
80 }
81
82 events.sort_by_key(|e| e.timestamp);
83 Ok(events)
84 }
85
86 pub fn remove_file(&self, staging_dir: &Path) -> Result<(), std::io::Error> {
88 let filename = format!("{}.json", self.id);
89 let path = staging_dir.join(filename);
90 fs::remove_file(path)
91 }
92}