digdigdig3_station/storage/
event_log.rs1use std::fs::{create_dir_all, File, OpenOptions};
11use std::io::{BufReader, Read, Write};
12use std::path::PathBuf;
13
14use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
15
16pub struct EventLog {
18 root: PathBuf,
19}
20
21pub struct EventRecord<'a> {
23 pub ts_ms: i64,
25 pub payload: &'a [u8],
27}
28
29impl EventLog {
30 pub fn new(root: impl Into<PathBuf>) -> std::io::Result<Self> {
32 let root = root.into();
33 create_dir_all(&root)?;
34 Ok(Self { root })
35 }
36
37 pub fn append(
41 &self,
42 symbol: &str,
43 stream_kind: &str,
44 record: &EventRecord<'_>,
45 ) -> std::io::Result<()> {
46 let dir = self.root.join(symbol);
47 create_dir_all(&dir)?;
48 let path = dir.join(format!("{stream_kind}.bin"));
49
50 let mut f = OpenOptions::new()
51 .create(true)
52 .append(true)
53 .open(&path)?;
54
55 let mut buf = Vec::with_capacity(8 + 4 + record.payload.len());
56 buf.write_i64::<LittleEndian>(record.ts_ms)?;
57 buf.write_u32::<LittleEndian>(record.payload.len() as u32)?;
58 buf.write_all(record.payload)?;
59 f.write_all(&buf)?;
60 Ok(())
61 }
62
63 pub fn read_all(
68 &self,
69 symbol: &str,
70 stream_kind: &str,
71 ) -> std::io::Result<Vec<(i64, Vec<u8>)>> {
72 let path = self.root.join(symbol).join(format!("{stream_kind}.bin"));
73 if !path.exists() {
74 return Ok(vec![]);
75 }
76 let mut reader = BufReader::new(File::open(&path)?);
77 let mut out = Vec::new();
78 loop {
79 let ts = match reader.read_i64::<LittleEndian>() {
80 Ok(v) => v,
81 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
82 Err(e) => return Err(e),
83 };
84 let len = match reader.read_u32::<LittleEndian>() {
85 Ok(v) => v as usize,
86 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
87 Err(e) => return Err(e),
88 };
89 let mut payload = vec![0u8; len];
90 match reader.read_exact(&mut payload) {
91 Ok(()) => out.push((ts, payload)),
92 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
93 Err(e) => return Err(e),
94 }
95 }
96 Ok(out)
97 }
98
99 pub fn read_range(
101 &self,
102 symbol: &str,
103 stream_kind: &str,
104 from_ms: i64,
105 to_ms: i64,
106 ) -> std::io::Result<Vec<(i64, Vec<u8>)>> {
107 let all = self.read_all(symbol, stream_kind)?;
108 Ok(all
109 .into_iter()
110 .filter(|(ts, _)| *ts >= from_ms && *ts <= to_ms)
111 .collect())
112 }
113
114 pub fn iter(
119 &self,
120 symbol: &str,
121 stream_kind: &str,
122 ) -> std::io::Result<EventLogIter> {
123 let path = self.root.join(symbol).join(format!("{stream_kind}.bin"));
124 let reader = if path.exists() {
125 Some(BufReader::new(File::open(&path)?))
126 } else {
127 None
128 };
129 Ok(EventLogIter { reader })
130 }
131}
132
133pub struct EventLogIter {
135 reader: Option<BufReader<File>>,
136}
137
138impl Iterator for EventLogIter {
139 type Item = std::io::Result<(i64, Vec<u8>)>;
140
141 fn next(&mut self) -> Option<Self::Item> {
142 let r = self.reader.as_mut()?;
143
144 let ts = match r.read_i64::<LittleEndian>() {
145 Ok(v) => v,
146 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return None,
147 Err(e) => return Some(Err(e)),
148 };
149 let len = match r.read_u32::<LittleEndian>() {
150 Ok(v) => v as usize,
151 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return None,
152 Err(e) => return Some(Err(e)),
153 };
154 let mut payload = vec![0u8; len];
155 match r.read_exact(&mut payload) {
156 Ok(()) => Some(Ok((ts, payload))),
157 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => None,
158 Err(e) => Some(Err(e)),
159 }
160 }
161}