1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
extern crate colored;

use std::path::Path;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::thread;

use entry::Entry;
use fsops;
use fsops::SyncOutcome::*;
use progress::Progress;
use workers::ProgressWorker;
use workers::SyncWorker;
use workers::WalkWorker;

#[derive(Default)]
pub struct Stats {
    pub num_files: u64,
    pub total_size: usize,

    pub num_synced: u64,
    pub up_to_date: u64,
    pub copied: u64,

    pub symlink_created: u64,
    pub symlink_updated: u64,
}

impl Stats {
    pub fn new() -> Stats {
        Stats {
            num_files: 0,
            total_size: 0,

            num_synced: 0,
            up_to_date: 0,
            copied: 0,

            symlink_created: 0,
            symlink_updated: 0,
        }
    }

    pub fn add_outcome(&mut self, outcome: &fsops::SyncOutcome) {
        self.num_synced += 1;
        match outcome {
            FileCopied => self.copied += 1,
            UpToDate => self.up_to_date += 1,
            SymlinkUpdated => self.symlink_updated += 1,
            SymlinkCreated => self.symlink_created += 1,
        }
    }
}

#[derive(Copy, Clone)]
pub struct SyncOptions {
    pub preserve_permissions: bool,
}

impl SyncOptions {
    fn new() -> SyncOptions {
        SyncOptions {
            preserve_permissions: true,
        }
    }
}

pub struct Syncer {
    source: PathBuf,
    destination: PathBuf,
    options: SyncOptions,
}

impl Syncer {
    pub fn new(source: &Path, destination: &Path) -> Syncer {
        Syncer {
            source: source.to_path_buf(),
            destination: destination.to_path_buf(),
            options: SyncOptions::new(),
        }
    }

    pub fn preserve_permissions(&mut self, preserve_permissions: bool) {
        self.options.preserve_permissions = preserve_permissions;
    }

    pub fn sync(self) -> Result<Stats, String> {
        let (walker_entry_output, syncer_input) = channel::<Entry>();
        let (walker_stats_output, progress_input) = channel::<Progress>();
        let progress_output = walker_stats_output.clone();

        let walk_worker = WalkWorker::new(&self.source, walker_entry_output, walker_stats_output);
        let sync_worker = SyncWorker::new(
            &self.source,
            &self.destination,
            syncer_input,
            progress_output,
        );
        let progress_worker = ProgressWorker::new(progress_input);

        let walker_thread = thread::spawn(move || walk_worker.start());
        let syncer_thread = thread::spawn(move || sync_worker.start(self.options));
        let progress_thread = thread::spawn(|| progress_worker.start());

        let walker_outcome = walker_thread.join();
        let syncer_outcome = syncer_thread.join();
        let progress_outcome = progress_thread.join();

        if walker_outcome.is_err() {
            return Err("Could not join walker thread".to_string());
        }

        if syncer_outcome.is_err() {
            return Err("Could not join syncer thread".to_string());
        }
        let syncer_result = syncer_outcome.unwrap();
        if syncer_result.is_err() {
            return Err(format!("{}", syncer_result.err().unwrap()));
        }

        if progress_outcome.is_err() {
            return Err("Could not join progress thread".to_string());
        }
        Ok(progress_outcome.unwrap())
    }
}