rusync/workers/
sync_worker.rs

1use std::fs;
2use std::path::Path;
3use std::path::PathBuf;
4use std::sync::mpsc::{Receiver, Sender};
5
6use anyhow::{Context, Error};
7
8use crate::entry::Entry;
9use crate::fsops;
10use crate::fsops::SyncOutcome;
11use crate::progress::ProgressMessage;
12use crate::sync::SyncOptions;
13
14pub struct SyncWorker {
15    input: Receiver<Entry>,
16    output: Sender<ProgressMessage>,
17    source: PathBuf,
18    destination: PathBuf,
19}
20
21impl SyncWorker {
22    pub fn new(
23        source: &Path,
24        destination: &Path,
25        input: Receiver<Entry>,
26        output: Sender<ProgressMessage>,
27    ) -> SyncWorker {
28        SyncWorker {
29            source: source.to_path_buf(),
30            destination: destination.to_path_buf(),
31            input,
32            output,
33        }
34    }
35
36    pub fn start(self, opts: SyncOptions) -> Result<(), Error> {
37        for entry in self.input.iter() {
38            let sync_outcome = self.sync(&entry, opts);
39            let progress_message = match sync_outcome {
40                Ok(s) => ProgressMessage::DoneSyncing(s),
41                Err(e) => ProgressMessage::SyncError {
42                    entry: entry.description().to_string(),
43                    details: format!("{:#}", e),
44                },
45            };
46            self.output.send(progress_message)?;
47        }
48        Ok(())
49    }
50
51    fn create_missing_dest_dirs(&self, rel_path: &Path) -> Result<(), Error> {
52        let parent_rel_path = rel_path
53            .parent()
54            .expect("dest directory should have a parent");
55        let to_create = self.destination.join(parent_rel_path);
56        fs::create_dir_all(&to_create)
57            .with_context(|| format!("Could not create '{}'", to_create.display()))?;
58        Ok(())
59    }
60
61    fn sync(&self, src_entry: &Entry, opts: SyncOptions) -> Result<SyncOutcome, Error> {
62        let rel_path = fsops::get_rel_path(src_entry.path(), &self.source);
63        self.create_missing_dest_dirs(&rel_path)?;
64        let desc = rel_path.to_string_lossy();
65
66        let dest_path = self.destination.join(&rel_path);
67        let dest_entry = Entry::new(&desc, &dest_path);
68        let outcome = fsops::sync_entries(&self.output, src_entry, &dest_entry)?;
69        #[cfg(unix)]
70        {
71            if opts.preserve_permissions {
72                fsops::copy_permissions(src_entry, &dest_entry)?;
73            }
74        }
75        Ok(outcome)
76    }
77}