1use anyhow::Result;
9use chrono::{DateTime, Datelike, Timelike, Utc};
10use serde::{Deserialize, Serialize};
11use sha2::{Digest, Sha256};
12use std::fs::{self, OpenOptions};
13use std::io::Write;
14use std::path::{Path, PathBuf};
15use std::time::{SystemTime, UNIX_EPOCH};
16
17pub mod operations;
18pub mod tracker;
19
20pub use operations::*;
21pub use tracker::*;
22
23pub struct FileHistoryConfig {
25 pub base_dir: PathBuf,
27 pub auto_create: bool,
29 pub prefer_append: bool,
31}
32
33impl Default for FileHistoryConfig {
34 fn default() -> Self {
35 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
36 Self {
37 base_dir: cwd.join(".st").join("filehistory"),
38 auto_create: true,
39 prefer_append: true,
40 }
41 }
42}
43
44pub fn get_time_bucket() -> (String, u64) {
46 let now = SystemTime::now()
47 .duration_since(UNIX_EPOCH)
48 .unwrap()
49 .as_secs();
50
51 let datetime = DateTime::<Utc>::from(UNIX_EPOCH + std::time::Duration::from_secs(now));
52 let minute = datetime.minute() / 10 * 10; let filename = format!(
55 "{:04}{:02}{:02}_{:02}{:02}",
56 datetime.year(),
57 datetime.month(),
58 datetime.day(),
59 datetime.hour(),
60 minute
61 );
62
63 (filename, now)
64}
65
66pub fn hash_file(path: &Path) -> Result<String> {
68 let contents = fs::read(path)?;
69 let mut hasher = Sha256::new();
70 hasher.update(&contents);
71 Ok(format!("{:x}", hasher.finalize()))
72}
73
74pub fn get_project_id(path: &Path) -> Result<String> {
76 let canonical = path.canonicalize()?;
77 let mut hasher = Sha256::new();
78 hasher.update(canonical.to_string_lossy().as_bytes());
79 Ok(format!("{:x}", hasher.finalize())[..16].to_string())
80}