Skip to main content

pijul_core/output/
mod.rs

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
107// Why can't I just derive Debug for `FileError`? The following is
108// what I expect the derived impl would be.
109impl<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    // First, get the basename of the path we're outputting.
231    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            // The following was supposed to be a panic, but actually it
265            // doesn't need to be one, there just wasn't any file to
266            // output.
267            /*
268            let mut edge = None;
269            for e in iter_adjacent(
270                txn,
271                channel,
272                *name_vertex,
273                EdgeFlags::FOLDER,
274                EdgeFlags::all(),
275            )? {
276                let e = e?;
277                if !e.flag().contains(EdgeFlags::PARENT) {
278                    edge = Some(e);
279                    break;
280                }
281            }
282            let e = edge.unwrap();
283            let mut f = std::fs::File::create("debug_output").unwrap();
284            debug_root(txn, channel, e.dest().inode_vertex(), &mut f, false).unwrap();
285            panic!("no child");
286            */
287        }
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}