ccsync_core/comparison/
timestamp.rs1use std::fs;
4use std::path::Path;
5use std::time::SystemTime;
6
7use anyhow::Context;
8
9use crate::error::Result;
10
11pub struct TimestampComparator;
13
14impl Default for TimestampComparator {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl TimestampComparator {
21 #[must_use]
23 pub const fn new() -> Self {
24 Self
25 }
26
27 pub fn is_newer(source: &Path, destination: &Path) -> Result<bool> {
33 let source_time = Self::get_modified_time(source)?;
34 let dest_time = Self::get_modified_time(destination)?;
35
36 Ok(source_time > dest_time)
37 }
38
39 pub fn get_modified_time(path: &Path) -> Result<SystemTime> {
45 let metadata = fs::metadata(path)
46 .with_context(|| format!("Failed to read metadata for: {}", path.display()))?;
47
48 metadata
49 .modified()
50 .with_context(|| format!("Failed to get modification time for: {}", path.display()))
51 }
52
53 pub fn compare_times(source: &Path, destination: &Path) -> Result<std::cmp::Ordering> {
59 let source_time = Self::get_modified_time(source)?;
60 let dest_time = Self::get_modified_time(destination)?;
61
62 Ok(source_time.cmp(&dest_time))
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use std::fs;
70 use std::thread;
71 use std::time::Duration;
72 use tempfile::TempDir;
73
74 #[test]
75 fn test_identical_timestamps() {
76 let tmp = TempDir::new().unwrap();
77 let file1 = tmp.path().join("file1.txt");
78 let file2 = tmp.path().join("file2.txt");
79
80 fs::write(&file1, "content").unwrap();
81
82 fs::copy(&file1, &file2).unwrap();
84
85 let _comparator = TimestampComparator::new();
86 let ordering = TimestampComparator::compare_times(&file1, &file2).unwrap();
87
88 assert!(
92 matches!(
93 ordering,
94 std::cmp::Ordering::Equal | std::cmp::Ordering::Less
95 ),
96 "Expected Equal or Less for copied file timestamps, got {:?}",
97 ordering
98 );
99 }
100
101 #[test]
102 fn test_source_newer() {
103 let tmp = TempDir::new().unwrap();
104 let file1 = tmp.path().join("old.txt");
105 let file2 = tmp.path().join("new.txt");
106
107 fs::write(&file1, "old content").unwrap();
109
110 thread::sleep(Duration::from_millis(10));
112
113 fs::write(&file2, "new content").unwrap();
115
116 let _comparator = TimestampComparator::new();
117 let is_newer = TimestampComparator::is_newer(&file2, &file1).unwrap();
118
119 assert!(is_newer, "file2 should be newer than file1");
120 }
121
122 #[test]
123 fn test_destination_newer() {
124 let tmp = TempDir::new().unwrap();
125 let file1 = tmp.path().join("new.txt");
126 let file2 = tmp.path().join("old.txt");
127
128 fs::write(&file2, "old content").unwrap();
130
131 thread::sleep(Duration::from_millis(10));
133
134 fs::write(&file1, "new content").unwrap();
136
137 let _comparator = TimestampComparator::new();
138 let is_newer = TimestampComparator::is_newer(&file1, &file2).unwrap();
139
140 assert!(is_newer, "file1 should be newer than file2");
141 }
142
143 #[test]
144 fn test_get_modified_time() {
145 let tmp = TempDir::new().unwrap();
146 let file = tmp.path().join("file.txt");
147 fs::write(&file, "content").unwrap();
148
149 let time = TimestampComparator::get_modified_time(&file);
150 assert!(time.is_ok());
151 }
152
153 #[test]
154 fn test_nonexistent_file() {
155 let tmp = TempDir::new().unwrap();
156 let file = tmp.path().join("nonexistent.txt");
157
158 let result = TimestampComparator::get_modified_time(&file);
159 assert!(result.is_err());
160 }
161}