rusty_bin/monitor/storage/
naming.rs1use rusty_common::time::days_since_epoch_to_date;
5use smartstring::alias::String as SmartString;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
10pub struct SimpleDate {
11 pub year: u32,
13 pub month: u32,
15 pub day: u32,
17}
18
19impl SimpleDate {
20 #[must_use]
22 pub fn new(year: u32, month: u32, day: u32) -> Option<Self> {
23 if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
24 return None;
25 }
26 Some(Self { year, month, day })
27 }
28
29 #[must_use]
31 pub fn from_system_time(time: SystemTime) -> Self {
32 let duration = time.duration_since(UNIX_EPOCH).unwrap_or_default();
33 let days = duration.as_secs() / 86400;
34 let (year, month, day) = days_since_epoch_to_date(days);
35 Self { year, month, day }
36 }
37
38 #[must_use]
40 pub fn today() -> Self {
41 Self::from_system_time(SystemTime::now())
42 }
43
44 #[must_use]
46 pub fn format_yyyymmdd(&self) -> String {
47 format!("{:04}{:02}{:02}", self.year, self.month, self.day)
48 }
49
50 #[must_use]
52 pub fn parse_yyyymmdd(date_str: &str) -> Option<Self> {
53 if date_str.len() != 8 {
54 return None;
55 }
56 let year = date_str[0..4].parse().ok()?;
57 let month = date_str[4..6].parse().ok()?;
58 let day = date_str[6..8].parse().ok()?;
59 Self::new(year, month, day)
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct FileNaming {
66 pub exchange: SmartString,
68 pub symbol: SmartString,
70 pub data_type: String,
72 pub date: SimpleDate,
74 pub extension: String,
76}
77
78impl FileNaming {
79 #[must_use]
81 pub fn new(
82 exchange: impl Into<SmartString>,
83 symbol: impl Into<SmartString>,
84 data_type: impl Into<String>,
85 date: SimpleDate,
86 ) -> Self {
87 Self {
88 exchange: exchange.into(),
89 symbol: symbol.into(),
90 data_type: data_type.into(),
91 date,
92 extension: "fb".to_string(),
93 }
94 }
95
96 #[must_use]
98 pub fn filename(&self) -> String {
99 format!(
100 "{}_{}_{}_{}.{}",
101 self.exchange,
102 self.symbol,
103 self.data_type,
104 self.date.format_yyyymmdd(),
105 self.extension
106 )
107 }
108
109 #[must_use]
111 pub fn compressed_filename(&self) -> String {
112 format!("{}.zst", self.filename())
113 }
114
115 #[must_use]
117 pub fn metadata_filename(&self) -> String {
118 format!(
119 "{}_{}_metadata.json",
120 self.exchange,
121 self.date.format_yyyymmdd()
122 )
123 }
124
125 #[must_use]
127 pub fn parse_filename(filename: &str) -> Option<Self> {
128 let parts: Vec<&str> = filename.split('_').collect();
129 if parts.len() < 4 {
130 return None;
131 }
132
133 let exchange = parts[0];
134 let symbol = parts[1];
135 let data_type = parts[2];
136
137 let date_ext = parts[3];
139 let date_parts: Vec<&str> = date_ext.split('.').collect();
140 if date_parts.len() < 2 {
141 return None;
142 }
143
144 let date_str = date_parts[0];
145 let extension = date_parts[1];
146
147 let date = SimpleDate::parse_yyyymmdd(date_str)?;
148
149 Some(Self {
150 exchange: exchange.into(),
151 symbol: symbol.into(),
152 data_type: data_type.to_string(),
153 date,
154 extension: extension.to_string(),
155 })
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn test_simple_date() {
165 let date = SimpleDate::new(2024, 12, 1).unwrap();
166 assert_eq!(date.format_yyyymmdd(), "20241201");
167
168 let parsed = SimpleDate::parse_yyyymmdd("20241201").unwrap();
169 assert_eq!(parsed, date);
170 }
171
172 #[test]
173 fn test_file_naming() {
174 let naming = FileNaming::new(
175 "binance",
176 "BTCUSDT",
177 "trades",
178 SimpleDate::new(2024, 12, 1).unwrap(),
179 );
180
181 assert_eq!(naming.filename(), "binance_BTCUSDT_trades_20241201.fb");
182 assert_eq!(
183 naming.compressed_filename(),
184 "binance_BTCUSDT_trades_20241201.fb.zst"
185 );
186 }
187
188 #[test]
189 fn test_filename_parsing() {
190 let filename = "binance_BTCUSDT_trades_20241201.fb";
191 let naming = FileNaming::parse_filename(filename).unwrap();
192
193 assert_eq!(naming.exchange, "binance");
194 assert_eq!(naming.symbol, "BTCUSDT");
195 assert_eq!(naming.data_type, "trades");
196 assert_eq!(naming.date, SimpleDate::new(2024, 12, 1).unwrap());
197 assert_eq!(naming.extension, "fb");
198 }
199}