1use crate::HashMap;
2use crate::changestore::{ChangeStore, FileMetadata};
3use crate::path;
4use crate::pristine::*;
5
6mod working_copy;
7pub use working_copy::*;
8mod archive;
9pub use archive::*;
10
11#[derive(Error)]
12pub enum OutputError<
13 ChangestoreError: std::error::Error + 'static,
14 T: GraphTxnT + TreeTxnT,
15 W: std::error::Error + Send + 'static,
16> {
17 WorkingCopy(W),
18 Pristine(#[from] PristineOutputError<ChangestoreError, T>),
19 SmallString(#[from] crate::small_string::Error),
20}
21
22impl<C: std::error::Error, T: GraphTxnT + TreeTxnT, W: std::error::Error + Send> std::fmt::Debug
23 for OutputError<C, T, W>
24{
25 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
26 match self {
27 OutputError::WorkingCopy(e) => std::fmt::Debug::fmt(e, fmt),
28 OutputError::Pristine(e) => std::fmt::Debug::fmt(e, fmt),
29 OutputError::SmallString(e) => std::fmt::Debug::fmt(e, fmt),
30 }
31 }
32}
33
34impl<C: std::error::Error, T: GraphTxnT + TreeTxnT, W: std::error::Error + Send> std::fmt::Display
35 for OutputError<C, T, W>
36{
37 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
38 match self {
39 OutputError::WorkingCopy(e) => std::fmt::Display::fmt(e, fmt),
40 OutputError::Pristine(e) => std::fmt::Display::fmt(e, fmt),
41 OutputError::SmallString(e) => std::fmt::Display::fmt(e, fmt),
42 }
43 }
44}
45
46#[derive(Error)]
47pub enum PristineOutputError<ChangestoreError: std::error::Error, T: GraphTxnT + TreeTxnT> {
48 Channel(#[from] TxnErr<T::GraphError>),
49 Tree(#[from] TreeErr<T::TreeError>),
50 Changestore(ChangestoreError),
51 Io(#[from] std::io::Error),
52 Fs(#[from] crate::fs::FsError<T>),
53}
54
55impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> std::fmt::Debug for PristineOutputError<C, T> {
56 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
57 match self {
58 PristineOutputError::Channel(e) => std::fmt::Debug::fmt(e, fmt),
59 PristineOutputError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
60 PristineOutputError::Changestore(e) => std::fmt::Debug::fmt(e, fmt),
61 PristineOutputError::Io(e) => std::fmt::Debug::fmt(e, fmt),
62 PristineOutputError::Fs(e) => std::fmt::Debug::fmt(e, fmt),
63 }
64 }
65}
66
67impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> std::fmt::Display
68 for PristineOutputError<C, T>
69{
70 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
71 match self {
72 PristineOutputError::Channel(e) => std::fmt::Display::fmt(e, fmt),
73 PristineOutputError::Tree(e) => std::fmt::Display::fmt(e, fmt),
74 PristineOutputError::Changestore(e) => std::fmt::Display::fmt(e, fmt),
75 PristineOutputError::Io(e) => std::fmt::Display::fmt(e, fmt),
76 PristineOutputError::Fs(e) => std::fmt::Display::fmt(e, fmt),
77 }
78 }
79}
80
81impl<C: std::error::Error, T: GraphTxnT + TreeTxnT, W: std::error::Error + Send>
82 From<TxnErr<T::GraphError>> for OutputError<C, T, W>
83{
84 fn from(e: TxnErr<T::GraphError>) -> Self {
85 OutputError::Pristine(e.into())
86 }
87}
88
89impl<C: std::error::Error, T: GraphTxnT + TreeTxnT, W: std::error::Error + Send>
90 From<TreeErr<T::TreeError>> for OutputError<C, T, W>
91{
92 fn from(e: TreeErr<T::TreeError>) -> Self {
93 OutputError::Pristine(e.into())
94 }
95}
96
97#[derive(Error)]
98pub enum FileError<ChangestoreError: std::error::Error + std::fmt::Debug + 'static, T: GraphTxnT> {
99 #[error(transparent)]
100 Changestore(ChangestoreError),
101 #[error(transparent)]
102 Txn(#[from] TxnErr<T::GraphError>),
103 #[error(transparent)]
104 Io(#[from] std::io::Error),
105}
106
107impl<ChangestoreError: std::error::Error + std::fmt::Debug + 'static, T: GraphTxnT> std::fmt::Debug
110 for FileError<ChangestoreError, T>
111{
112 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
113 match self {
114 FileError::Changestore(c) => std::fmt::Debug::fmt(c, fmt),
115 FileError::Txn(c) => std::fmt::Debug::fmt(c, fmt),
116 FileError::Io(c) => std::fmt::Debug::fmt(c, fmt),
117 }
118 }
119}
120
121impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> From<FileError<C, T>>
122 for PristineOutputError<C, T>
123{
124 fn from(e: FileError<C, T>) -> Self {
125 match e {
126 FileError::Changestore(e) => PristineOutputError::Changestore(e),
127 FileError::Io(e) => PristineOutputError::Io(e),
128 FileError::Txn(t) => PristineOutputError::Channel(t),
129 }
130 }
131}
132
133#[derive(Debug, Clone)]
134struct OutputItem {
135 parent: Inode,
136 path: String,
137 tmp: Option<String>,
138 meta: InodeMetadata,
139 pos: Position<ChangeId>,
140 is_zombie: Option<Vec<Hash>>,
141}
142
143#[allow(clippy::too_many_arguments)]
144fn collect_children<T: GraphTxnT + TreeTxnT, P: ChangeStore>(
145 txn: &T,
146 changes: &P,
147 channel: &T::Graph,
148 inode_pos: Position<ChangeId>,
149 inode: Inode,
150 path: &str,
151 tmp: Option<&str>,
152 prefix_basename: Option<&str>,
153 files: &mut HashMap<String, Vec<(Vertex<ChangeId>, OutputItem)>>,
154) -> Result<(), PristineOutputError<P::Error, T>> {
155 debug!("path = {:?}, inode_pos = {:?}", path, inode_pos);
156 debug!("prefix_basename = {:?}", prefix_basename);
157 for e in iter_adjacent(
158 txn,
159 channel,
160 inode_pos.inode_vertex(),
161 EdgeFlags::FOLDER,
162 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
163 )? {
164 let e = e?;
165 debug!("e = {:?}", e);
166 let name_vertex = txn.find_block(channel, e.dest()).unwrap();
167 if name_vertex.start != name_vertex.end {
168 debug!("name_vertex: {:?} {:?}", e, name_vertex);
169 collect(
170 txn,
171 changes,
172 channel,
173 inode,
174 path,
175 tmp,
176 prefix_basename,
177 files,
178 name_vertex,
179 )?
180 } else {
181 let inode_pos = iter_adjacent(
182 txn,
183 channel,
184 *name_vertex,
185 EdgeFlags::FOLDER,
186 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
187 )?
188 .next()
189 .unwrap()?
190 .dest();
191 for e in iter_adjacent(
192 txn,
193 channel,
194 inode_pos.inode_vertex(),
195 EdgeFlags::FOLDER,
196 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
197 )? {
198 let e = e?;
199 debug!("e' = {:?}", e);
200 let name_vertex = txn.find_block(channel, e.dest()).unwrap();
201 collect(
202 txn,
203 changes,
204 channel,
205 inode,
206 path,
207 tmp,
208 prefix_basename,
209 files,
210 name_vertex,
211 )?
212 }
213 }
214 }
215 Ok(())
216}
217
218#[allow(clippy::too_many_arguments)]
219fn collect<T: GraphTxnT + TreeTxnT, P: ChangeStore>(
220 txn: &T,
221 changes: &P,
222 channel: &T::Graph,
223 inode: Inode,
224 path: &str,
225 tmp: Option<&str>,
226 prefix_basename: Option<&str>,
227 files: &mut HashMap<String, Vec<(Vertex<ChangeId>, OutputItem)>>,
228 name_vertex: &Vertex<ChangeId>,
229) -> Result<(), PristineOutputError<P::Error, T>> {
230 let mut name_buf = vec![0; name_vertex.end - name_vertex.start];
232 let FileMetadata {
233 basename,
234 metadata: perms,
235 ..
236 } = changes
237 .get_file_meta(
238 |h| txn.get_external(&h).ok().map(|x| x.into()),
239 *name_vertex,
240 &mut name_buf,
241 )
242 .map_err(PristineOutputError::Changestore)?;
243 debug!("filename: {:?} {:?}", perms, basename);
244 let mut name = path.to_string();
245 if let Some(next) = prefix_basename
246 && next != basename
247 {
248 debug!("next = {:?} basename = {:?}", next, basename);
249 return Ok(());
250 }
251 path::push(&mut name, basename);
252 let child = match iter_adjacent(
253 txn,
254 channel,
255 *name_vertex,
256 EdgeFlags::FOLDER,
257 EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
258 )?
259 .next()
260 {
261 Some(child) => child?,
262 _ => {
263 return Ok(());
264 }
288 };
289
290 debug!("child: {:?}", child);
291 let v = files.entry(name).or_default();
292 v.push((
293 *name_vertex,
294 OutputItem {
295 parent: inode,
296 path: path.to_string(),
297 tmp: tmp.map(String::from),
298 meta: perms,
299 pos: child.dest(),
300 is_zombie: is_zombie(txn, channel, child.dest())?,
301 },
302 ));
303 Ok(())
304}
305
306fn is_zombie<T: GraphTxnT>(
307 txn: &T,
308 channel: &T::Graph,
309 pos: Position<ChangeId>,
310) -> Result<Option<Vec<Hash>>, TxnErr<T::GraphError>> {
311 let f = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::DELETED;
312 match iter_adjacent(txn, channel, pos.inode_vertex(), f, f | EdgeFlags::BLOCK)?.next() {
313 Some(n) => {
314 let _n = n?;
315 let mut id = Vec::new();
316 let f = EdgeFlags::FOLDER | EdgeFlags::PARENT;
317 for e in iter_adjacent(
318 txn,
319 channel,
320 pos.inode_vertex(),
321 f,
322 f | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
323 )? {
324 let e = e?;
325 if e.flag().contains(f) {
326 id.push(
327 txn.get_external(&e.introduced_by())
328 .optional()?
329 .unwrap()
330 .into(),
331 )
332 }
333 }
334 if id.is_empty() {
335 Ok(None)
336 } else {
337 Ok(Some(id))
338 }
339 }
340 _ => Ok(None),
341 }
342}
343
344pub fn output_file<
345 T: TreeTxnT + ChannelTxnT,
346 C: crate::changestore::ChangeStore,
347 V: crate::vertex_buffer::VertexBuffer,
348>(
349 changes: &C,
350 txn: &ArcTxn<T>,
351 channel: &ChannelRef<T>,
352 v0: Position<ChangeId>,
353 out: &mut V,
354) -> Result<(), FileError<C::Error, T>> {
355 let mut forward = Vec::new();
356 let mut graph = {
357 let txn = txn.read();
358 let channel = channel.read();
359 crate::alive::retrieve(&*txn, txn.graph(&*channel), v0, false)?
360 };
361 crate::alive::output_graph(changes, txn, channel, out, &mut graph, &mut forward)?;
362 Ok(())
363}