1use chrono::{DateTime, SecondsFormat, Utc};
23use std::ffi::OsString;
24use std::path::{Path, PathBuf};
25
26#[must_use]
27pub fn label_file_name(path: &Path, label: &str) -> Option<PathBuf> {
28 let mut file_name = OsString::new();
29
30 if let Some(s) = path.file_stem() {
31 file_name.push(s);
32 } else {
33 return None;
34 }
35
36 file_name.push("-");
37 file_name.push(label);
38
39 if let Some(s) = path.extension() {
40 file_name.push(".");
41 file_name.push(s);
42 }
43
44 Some(path.with_file_name(file_name))
45}
46
47#[must_use]
48pub fn file_name_safe_timestamp(dt: &DateTime<Utc>) -> String {
49 dt.to_rfc3339_opts(SecondsFormat::Millis, true)
50 .replace(['-', ':', '.'], "")
51}
52
53#[cfg(test)]
54mod tests {
55 use super::{file_name_safe_timestamp, label_file_name};
56 use chrono::{TimeZone, Utc};
57 use rstest::rstest;
58 use std::path::PathBuf;
59
60 #[rstest]
61 #[case(Some(PathBuf::from("/aaa/bbb/ccc-ddd.txt")), "/aaa/bbb/ccc.txt", "ddd")]
62 #[case(Some(PathBuf::from("/aaa/bbb/ccc-ddd")), "/aaa/bbb/ccc", "ddd")]
63 #[case(Some(PathBuf::from("ccc-ddd.txt")), "ccc.txt", "ddd")]
64 #[case(Some(PathBuf::from("ccc-ddd")), "ccc", "ddd")]
65 fn label_file_name_basics(
66 #[case] expected_path: Option<PathBuf>,
67 #[case] path: PathBuf,
68 #[case] label: &str,
69 ) {
70 assert_eq!(expected_path, label_file_name(&path, label));
71 }
72
73 #[test]
74 fn file_name_safe_timestamp_basics() {
75 let dt = Utc
76 .with_ymd_and_hms(2019, 3, 17, 16, 43, 0)
77 .single()
78 .expect("must be valid");
79 assert_eq!("20190317T164300000Z", file_name_safe_timestamp(&dt));
80 }
81}