1use std::{collections::HashSet, fs, path::Path};
8
9use crate::{fs_atomic::write_file_atomic, object::ChangeId, store::Result};
10
11pub struct ShallowInfo {
13 path: std::path::PathBuf,
14 shallow_states: HashSet<ChangeId>,
15}
16
17impl ShallowInfo {
18 pub fn load(heddle_dir: &Path) -> Result<Self> {
20 let path = heddle_dir.join("shallow");
21 let shallow_states = if path.exists() {
22 let contents = fs::read_to_string(&path)?;
23 contents
24 .lines()
25 .filter_map(|line| {
26 let line = line.trim();
27 if line.is_empty() || line.starts_with('#') {
28 None
29 } else {
30 ChangeId::parse(line).ok()
31 }
32 })
33 .collect()
34 } else {
35 HashSet::new()
36 };
37
38 Ok(Self {
39 path,
40 shallow_states,
41 })
42 }
43
44 pub fn is_shallow(&self, id: &ChangeId) -> bool {
46 self.shallow_states.contains(id)
47 }
48
49 pub fn shallow_states(&self) -> &HashSet<ChangeId> {
51 &self.shallow_states
52 }
53
54 pub fn add_shallow(&mut self, id: ChangeId) -> Result<()> {
56 if self.shallow_states.insert(id) {
57 self.save()?;
58 }
59 Ok(())
60 }
61
62 pub fn remove_shallow(&mut self, id: &ChangeId) -> Result<()> {
64 if self.shallow_states.remove(id) {
65 self.save()?;
66 }
67 Ok(())
68 }
69
70 fn save(&self) -> Result<()> {
72 let contents: String = self
73 .shallow_states
74 .iter()
75 .map(|id| format!("{}\n", id.to_string_full()))
76 .collect();
77
78 write_file_atomic(&self.path, contents.as_bytes())?;
79 Ok(())
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use tempfile::TempDir;
86
87 use super::*;
88
89 #[test]
90 fn test_shallow_save_rewrites_file_without_temp_residue() {
91 let temp_dir = TempDir::new().unwrap();
92 let heddle_dir = temp_dir.path().join(".heddle");
93 fs::create_dir_all(&heddle_dir).unwrap();
94
95 let mut shallow = ShallowInfo::load(&heddle_dir).unwrap();
96 let id = ChangeId::generate();
97 shallow.add_shallow(id).unwrap();
98
99 let reloaded = ShallowInfo::load(&heddle_dir).unwrap();
100 assert!(reloaded.is_shallow(&id));
101
102 let temp_entries = fs::read_dir(&heddle_dir)
103 .unwrap()
104 .filter_map(|entry| entry.ok())
105 .filter(|entry| entry.file_name().to_string_lossy().contains(".tmp-"))
106 .count();
107 assert_eq!(temp_entries, 0);
108 }
109}