1use chrono::{DateTime, SecondsFormat, Utc};
2use sha2::{Digest, Sha256};
3use std::fs::Metadata;
4use std::path::Path;
5use std::time::SystemTime;
6
7pub fn system_time_to_datetime(time: SystemTime) -> Option<DateTime<Utc>> {
8 Some(DateTime::<Utc>::from(time))
9}
10
11pub fn system_time_to_rfc3339(time: SystemTime) -> Option<DateTime<Utc>> {
12 system_time_to_datetime(time)
13}
14
15pub fn hash_path_metadata(path: &Path, metadata: &Metadata) -> String {
16 let mut hasher = Sha256::new();
17 hasher.update(path.to_string_lossy().as_bytes());
18 hasher.update(metadata.len().to_le_bytes());
19 if let Ok(modified) = metadata.modified()
20 && let Some(dt) = system_time_to_datetime(modified)
21 {
22 hasher.update(dt.to_rfc3339_opts(SecondsFormat::Micros, true).as_bytes());
23 }
24 format!("{:x}", hasher.finalize())
25}
26
27pub fn column_number_to_name(column: u32) -> String {
28 let mut column = column;
29 let mut name = String::new();
30 while column > 0 {
31 let rem = ((column - 1) % 26) as u8;
32 name.insert(0, (b'A' + rem) as char);
33 column = (column - 1) / 26;
34 }
35 name
36}
37
38pub fn cell_address(column: u32, row: u32) -> String {
39 format!("{}{}", column_number_to_name(column), row)
40}
41
42pub fn make_short_workbook_id(slug: &str, canonical_id: &str) -> String {
43 let mut slug_part: String = slug
44 .chars()
45 .filter(|c| c.is_ascii_alphanumeric())
46 .map(|c| c.to_ascii_lowercase())
47 .collect();
48 if slug_part.is_empty() {
49 slug_part = "wb".to_string();
50 }
51 if slug_part.len() > 12 {
52 slug_part.truncate(12);
53 }
54 let short_hash: String = canonical_id.chars().take(8).collect();
55 format!("{}-{}", slug_part, short_hash)
56}
57
58pub fn path_to_forward_slashes(path: &Path) -> String {
59 let raw = path.to_string_lossy();
60 if raw.contains('\\') {
61 raw.replace('\\', "/")
62 } else {
63 raw.into_owned()
64 }
65}