Skip to main content

ed_journals/modules/io/models/
log_path.rs

1use crate::modules::io::LogIOError;
2use chrono::NaiveDateTime;
3use lazy_static::lazy_static;
4use regex::Regex;
5use std::cmp::Ordering;
6use std::path::{Path, PathBuf};
7
8/// Path with the timestamp and part number parsed so that the paths can be ordered.
9#[derive(Debug, Clone)]
10pub struct LogPath {
11    pub path: PathBuf,
12    pub timestamp: NaiveDateTime,
13    pub part: u8,
14}
15
16#[cfg(not(feature = "legacy"))]
17type RegexList = [(Regex, &'static str); 1];
18
19#[cfg(feature = "legacy")]
20type RegexList = [(Regex, &'static str); 2];
21
22lazy_static! {
23    static ref FILE_NAME_REGEXES: RegexList = [
24        // Journal.YYYY-MM-DDTHHmmss.01.log
25        (Regex::new(r"Journal\.(\d{4}-\d{2}-\d{2}T\d+)\.(\d{2})\.log").unwrap(), "%Y-%m-%dT%H%M%S"),
26
27        // Journal.YYMMDDHHMMSS.01.log
28        #[cfg(feature = "legacy")]
29        (Regex::new(r"Journal\.(\d{12})\.(\d{2})\.log").unwrap(), "%y%m%d%H%M%S"),
30    ];
31}
32
33impl TryFrom<&Path> for LogPath {
34    type Error = LogIOError;
35
36    fn try_from(value: &Path) -> Result<Self, Self::Error> {
37        let file_name = value
38            .file_name()
39            .ok_or(LogIOError::MissingFileName)?
40            .to_str()
41            .ok_or(LogIOError::FailedToRepresentOsString)?;
42
43        for (regex, format) in FILE_NAME_REGEXES.iter() {
44            let Some(captures) = regex.captures(file_name) else {
45                continue;
46            };
47
48            let timestamp_str = captures
49                .get(1)
50                .expect("Regex should have already matched")
51                .as_str();
52
53            let timestamp = NaiveDateTime::parse_from_str(timestamp_str, format)
54                .map_err(LogIOError::FailedToParseLogTime)?;
55
56            let part = captures
57                .get(2)
58                .expect("Regex should have already matched")
59                .as_str()
60                .parse()
61                .map_err(LogIOError::FailedToParsePart)?;
62
63            return Ok(LogPath {
64                path: value.to_path_buf(),
65                timestamp,
66                part,
67            });
68        }
69
70        Err(LogIOError::IncorrectFileName)
71    }
72}
73
74impl AsRef<Path> for LogPath {
75    fn as_ref(&self) -> &Path {
76        self.path.as_path()
77    }
78}
79
80impl From<LogPath> for PathBuf {
81    fn from(val: LogPath) -> Self {
82        val.path
83    }
84}
85
86impl Eq for LogPath {}
87
88impl PartialEq for LogPath {
89    fn eq(&self, other: &Self) -> bool {
90        self.path.eq(&other.path)
91    }
92}
93
94impl PartialOrd for LogPath {
95    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
96        Some(self.cmp(other))
97    }
98}
99
100impl Ord for LogPath {
101    fn cmp(&self, other: &Self) -> Ordering {
102        self.timestamp
103            .cmp(&other.timestamp)
104            .then(self.part.cmp(&other.part))
105    }
106}