librojo/syncback/
fs_snapshot.rs1use std::{
2 collections::{HashMap, HashSet},
3 io,
4 path::{Path, PathBuf},
5};
6
7use memofs::Vfs;
8
9#[derive(Default)]
11pub struct FsSnapshot {
12 added_files: HashMap<PathBuf, Vec<u8>>,
14 added_dirs: HashSet<PathBuf>,
16 removed_files: HashSet<PathBuf>,
18 removed_dirs: HashSet<PathBuf>,
20}
21
22impl FsSnapshot {
23 pub fn new() -> Self {
25 Self {
26 added_files: HashMap::new(),
27 added_dirs: HashSet::new(),
28 removed_files: HashSet::new(),
29 removed_dirs: HashSet::new(),
30 }
31 }
32
33 pub fn with_added_file<P: AsRef<Path>>(mut self, path: P, data: Vec<u8>) -> Self {
36 self.added_files.insert(path.as_ref().to_path_buf(), data);
37 self
38 }
39
40 pub fn with_added_dir<P: AsRef<Path>>(mut self, path: P) -> Self {
43 self.added_dirs.insert(path.as_ref().to_path_buf());
44 self
45 }
46
47 #[inline]
49 pub fn merge(&mut self, other: Self) {
50 self.added_files.extend(other.added_files);
51 self.added_dirs.extend(other.added_dirs);
52 self.removed_files.extend(other.removed_files);
53 self.removed_dirs.extend(other.removed_dirs);
54 }
55
56 #[inline]
58 pub fn merge_with_filter<F>(&mut self, other: Self, mut predicate: F)
59 where
60 F: FnMut(&Path) -> bool,
61 {
62 self.added_files
63 .extend(other.added_files.into_iter().filter(|(k, _)| predicate(k)));
64 self.added_dirs
65 .extend(other.added_dirs.into_iter().filter(|p| predicate(p)));
66 self.removed_files
67 .extend(other.removed_files.into_iter().filter(|p| predicate(p)));
68 self.removed_dirs
69 .extend(other.removed_dirs.into_iter().filter(|p| predicate(p)));
70 }
71
72 pub fn add_file<P: AsRef<Path>>(&mut self, path: P, data: Vec<u8>) {
74 self.added_files.insert(path.as_ref().to_path_buf(), data);
75 }
76
77 pub fn add_dir<P: AsRef<Path>>(&mut self, path: P) {
79 self.added_dirs.insert(path.as_ref().to_path_buf());
80 }
81
82 pub fn remove_file<P: AsRef<Path>>(&mut self, path: P) {
84 self.removed_files.insert(path.as_ref().to_path_buf());
85 }
86
87 pub fn remove_dir<P: AsRef<Path>>(&mut self, path: P) {
89 self.removed_dirs.insert(path.as_ref().to_path_buf());
90 }
91
92 pub fn write_to_vfs<P: AsRef<Path>>(&self, base: P, vfs: &Vfs) -> io::Result<()> {
97 let mut lock = vfs.lock();
98
99 let base_path = base.as_ref();
100 for dir_path in &self.added_dirs {
101 match lock.create_dir_all(base_path.join(dir_path)) {
102 Ok(_) => (),
103 Err(err) if err.kind() == io::ErrorKind::AlreadyExists => (),
104 Err(err) => return Err(err),
105 };
106 }
107 for (path, contents) in &self.added_files {
108 lock.write(base_path.join(path), contents)?;
109 }
110 for dir_path in &self.removed_dirs {
111 lock.remove_dir_all(base_path.join(dir_path))?;
112 }
113 for path in &self.removed_files {
114 lock.remove_file(base_path.join(path))?;
115 }
116 drop(lock);
117
118 log::debug!(
119 "Wrote {} directories and {} files to the file system",
120 self.added_dirs.len(),
121 self.added_files.len()
122 );
123 log::debug!(
124 "Removed {} directories and {} files from the file system",
125 self.removed_dirs.len(),
126 self.removed_files.len()
127 );
128 Ok(())
129 }
130
131 #[inline]
133 pub fn is_empty(&self) -> bool {
134 self.added_files.is_empty()
135 && self.added_dirs.is_empty()
136 && self.removed_files.is_empty()
137 && self.removed_dirs.is_empty()
138 }
139
140 #[inline]
142 pub fn added_paths(&self) -> Vec<&Path> {
143 let mut list = Vec::with_capacity(self.added_files.len() + self.added_dirs.len());
144 list.extend(self.added_files());
145 list.extend(self.added_dirs());
146
147 list
148 }
149
150 #[inline]
152 pub fn removed_paths(&self) -> Vec<&Path> {
153 let mut list = Vec::with_capacity(self.removed_files.len() + self.removed_dirs.len());
154 list.extend(self.removed_files());
155 list.extend(self.removed_dirs());
156
157 list
158 }
159
160 #[inline]
162 pub fn added_files(&self) -> Vec<&Path> {
163 let mut added_files: Vec<_> = self.added_files.keys().map(PathBuf::as_path).collect();
164 added_files.sort_unstable();
165 added_files
166 }
167
168 #[inline]
170 pub fn added_dirs(&self) -> Vec<&Path> {
171 let mut added_dirs: Vec<_> = self.added_dirs.iter().map(PathBuf::as_path).collect();
172 added_dirs.sort_unstable();
173 added_dirs
174 }
175
176 #[inline]
178 pub fn removed_files(&self) -> Vec<&Path> {
179 let mut removed_files: Vec<_> = self.removed_files.iter().map(PathBuf::as_path).collect();
180 removed_files.sort_unstable();
181 removed_files
182 }
183
184 #[inline]
186 pub fn removed_dirs(&self) -> Vec<&Path> {
187 let mut removed_dirs: Vec<_> = self.removed_dirs.iter().map(PathBuf::as_path).collect();
188 removed_dirs.sort_unstable();
189 removed_dirs
190 }
191}