webserver_base/assets/
modified.rs1use std::path::{Path, PathBuf};
16use std::time::SystemTime;
17
18use chrono::{DateTime, Datelike, Utc};
19use tracing::error;
20
21#[must_use]
28pub fn content_modified(directories: &[&str]) -> Option<DateTime<Utc>> {
29 let newest: DateTime<Utc> = directories
30 .iter()
31 .filter_map(|directory| newest_mtime(Path::new(directory)))
32 .chain(current_exe_mtime())
33 .max()?;
34
35 let now: DateTime<Utc> = Utc::now();
36 if newest.year() <= 2000 || newest > now {
37 error!(
38 "content modification time is `{newest}`, which is implausible; \
39 omitting <lastmod>. Check that the build preserves file timestamps."
40 );
41 return None;
42 }
43
44 Some(newest)
45}
46
47fn newest_mtime(root: &Path) -> Option<DateTime<Utc>> {
49 if !root.is_dir() {
50 return None;
51 }
52
53 let mut newest: Option<DateTime<Utc>> = None;
54 let mut directories: Vec<std::path::PathBuf> = vec![root.to_path_buf()];
55
56 while let Some(directory) = directories.pop() {
57 let Ok(entries) = std::fs::read_dir(&directory) else {
58 continue;
59 };
60 for entry in entries.flatten() {
61 let path: PathBuf = entry.path();
62 if path.is_dir() {
63 directories.push(path);
64 continue;
65 }
66 let Ok(modified) = entry.metadata().and_then(|meta| meta.modified()) else {
67 continue;
68 };
69 let modified: DateTime<Utc> = modified.into();
70 if newest.is_none_or(|current| modified > current) {
71 newest = Some(modified);
72 }
73 }
74 }
75
76 newest
77}
78
79fn current_exe_mtime() -> Option<DateTime<Utc>> {
81 let exe: PathBuf = std::env::current_exe().ok()?;
82 let modified: SystemTime = std::fs::metadata(exe).ok()?.modified().ok()?;
83 Some(modified.into())
84}
85
86#[cfg(test)]
87mod tests {
88 use chrono::{DateTime, Utc};
89
90 use super::{content_modified, newest_mtime};
91
92 #[test]
93 fn a_directory_that_does_not_exist_contributes_nothing() {
94 let expected: Option<DateTime<Utc>> = None;
95 let actual: Option<DateTime<Utc>> =
96 newest_mtime(std::path::Path::new("/tmp/wsb-no-such-dir"));
97 assert_eq!(expected, actual);
98 }
99
100 #[test]
101 fn the_binarys_own_timestamp_is_enough_to_produce_a_plausible_date() {
102 let actual: Option<DateTime<Utc>> = content_modified(&["/tmp/wsb-no-such-dir"]);
105 assert!(actual.is_some());
106 }
107}