use std::path::Path;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::thread;
use anyhow::{anyhow, Error};
use crate::entry::Entry;
use crate::fsops;
use crate::fsops::SyncOutcome::*;
use crate::progress::{ProgressInfo, ProgressMessage};
use crate::workers::ProgressWorker;
use crate::workers::SyncWorker;
use crate::workers::WalkWorker;
#[derive(Debug)]
pub struct Stats {
pub num_files: u64,
pub total_size: usize,
pub total_transfered: u64,
pub num_synced: u64,
pub up_to_date: u64,
pub copied: u64,
pub errors: u64,
pub symlink_created: u64,
pub symlink_updated: u64,
pub duration: std::time::Duration,
start: std::time::Instant,
}
impl Stats {
#[allow(clippy::new_without_default)]
pub fn new() -> Stats {
Stats {
num_files: 0,
total_size: 0,
total_transfered: 0,
num_synced: 0,
up_to_date: 0,
copied: 0,
errors: 0,
symlink_created: 0,
symlink_updated: 0,
start: std::time::Instant::now(),
duration: std::time::Duration::new(0, 0),
}
}
pub fn start(&mut self) {
self.start = std::time::Instant::now();
}
pub fn stop(&mut self) {
let end = std::time::Instant::now();
self.duration = end - self.start;
}
pub fn duration(&self) -> std::time::Duration {
self.duration
}
pub fn add_error(&mut self) {
self.errors += 1;
}
#[doc(hidden)]
pub fn add_outcome(&mut self, outcome: &fsops::SyncOutcome) {
self.num_synced += 1;
match outcome {
FileCopied { size } => {
self.copied += 1;
self.total_transfered += size;
}
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 Default for SyncOptions {
fn default() -> Self {
Self {
preserve_permissions: true,
}
}
}
pub struct Syncer {
source: PathBuf,
destination: PathBuf,
options: SyncOptions,
progress_info: Box<dyn ProgressInfo + Send>,
}
impl Syncer {
pub fn new(
source: &Path,
destination: &Path,
options: SyncOptions,
progress_info: Box<dyn ProgressInfo + Send>,
) -> Syncer {
Syncer {
source: source.to_path_buf(),
destination: destination.to_path_buf(),
progress_info,
options,
}
}
pub fn sync(self) -> Result<Stats, Error> {
let (walker_entry_output, syncer_input) = channel::<Entry>();
let (walker_stats_output, progress_input) = channel::<ProgressMessage>();
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, self.progress_info);
let options = self.options;
let walker_thread = thread::spawn(move || walk_worker.start());
let syncer_thread = thread::spawn(move || sync_worker.start(options));
let progress_thread = thread::spawn(|| progress_worker.start());
walker_thread
.join()
.map_err(|e| anyhow!("Could not join walker thread: {:?}", e))?;
let syncer_result = syncer_thread
.join()
.map_err(|e| anyhow!("Could not join syncer thread: {:?}", e))?;
let progress_result = progress_thread
.join()
.map_err(|e| anyhow!("Could not join progress thread: {:?}", e))?;
syncer_result?;
Ok(progress_result)
}
}