use std::fs::{copy, create_dir, read_dir, remove_dir_all};
use std::fs::{DirEntry, File, Metadata};
use std::io::{Error, ErrorKind, Result};
use std::path::PathBuf;
use regex::Regex;
use crate::config::BackupTarget;
pub fn ignored(
path: &PathBuf,
metadata: &Metadata,
ignored_files: &[String],
ignored_folders: &[String],
) -> bool {
if metadata.is_file() {
for filter in ignored_files {
if filter.starts_with("r#") {
let r = Regex::new(&filter[2..]);
if let Ok(r) = r {
if r.is_match(path.file_name().unwrap().to_str().unwrap()) {
return true;
}
}
} else if path.file_name().unwrap().to_str().unwrap() == filter {
return true;
}
}
} else {
for filter in ignored_folders {
if filter.starts_with("r#") {
let r = Regex::new(&filter[2..]);
if let Ok(r) = r {
if r.is_match(path.as_os_str().to_str().unwrap()) {
return true;
}
}
} else if path.as_os_str().to_str().unwrap() == filter {
return true;
}
}
}
false
}
pub fn local_copy(target: BackupTarget) -> Result<i32> {
let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string();
let from = &target.path;
let to = &target.target_path;
let check_timestamp = if target.keep_num == 1 {
!target.always_copy
} else {
false
};
let ignored_files = &target.ignore_files;
let ignored_folders = &target.ignore_folders;
let mut num: i32 = 0;
let from_file: File = File::open(from)?;
if File::open(to).is_err() {
return Err(Error::new(
ErrorKind::NotFound,
"The destination is unavailable.",
));
};
if from_file.metadata().unwrap().is_file() {
if let Some(file_name) = from.file_name() {
if ignored(
from,
&from_file.metadata().unwrap(),
ignored_files,
ignored_folders,
) {
return Ok(0);
}
if let Ok(to_file) = File::open(to.join(file_name)) {
if check_timestamp
&& from_file.metadata().unwrap().modified().unwrap()
< to_file.metadata().unwrap().modified().unwrap()
{
return Ok(0);
}
}
num += 1;
copy(from, to.join(file_name))?;
}
} else {
let mut copy_queue: Vec<(DirEntry, PathBuf)> = Vec::new();
for dir_entry in read_dir(from)? {
if let Ok(dir_entry) = dir_entry {
let file_name = dir_entry.file_name();
if target.keep_num == 1 {
copy_queue.push((
dir_entry,
to.join(from.file_name().unwrap()).join(file_name),
));
} else {
let time_dir = to
.join(from.file_name().unwrap())
.join(&now)
.join(file_name);
copy_queue.push((dir_entry, time_dir));
}
}
}
if target.keep_num == 1 && File::open(to.join(from.file_name().unwrap())).is_err() {
create_dir(to.join(from.file_name().unwrap()))?;
} else if target.keep_num > 1 {
create_dir(to.join(from.file_name().unwrap()).join(&now))?;
clear_old(&to.join(from.file_name().unwrap()), target.keep_num)
}
while !copy_queue.is_empty() {
let entry = copy_queue.pop().unwrap();
let info: Metadata = entry.0.metadata().unwrap();
if info.is_file() {
if ignored(&entry.0.path(), &info, &ignored_files, &ignored_folders) {
continue;
}
if check_timestamp {
let copied_file = File::open(&entry.1);
if let Ok(copied_file) = copied_file {
if entry.0.metadata().unwrap().modified().unwrap()
< copied_file.metadata().unwrap().modified().unwrap()
{
continue;
}
}
}
if copy(entry.0.path(), &entry.1).is_ok() {
num += 1;
}
} else {
let entry_path = entry.0.path();
let striped_path = entry_path.strip_prefix(from);
if let Ok(striped_path) = striped_path {
if ignored(
&striped_path.to_path_buf(),
&info,
ignored_files,
ignored_folders,
) {
continue;
}
}
if File::open(&entry.1).is_err() {
create_dir(&entry.1).is_ok();
}
if let Ok(dir_entries) = read_dir(entry.0.path()) {
for e in dir_entries {
if let Ok(e) = e {
let target_path: PathBuf = entry.1.join(e.file_name());
copy_queue.push((e, target_path));
}
}
}
}
}
}
Ok(num)
}
fn clear_old(directory: &PathBuf, keep_num: i32) {
let file_opt = File::open(directory);
if let Ok(file) = file_opt {
if file.metadata().unwrap().is_file() {
return;
}
if let Ok(entries) = read_dir(directory) {
let mut dir_names: Vec<std::ffi::OsString> = entries
.map(|entry: Result<DirEntry>| {
if let Ok(entry) = entry {
entry.file_name()
} else {
panic!("Could not read the directory!")
}
})
.collect();
if dir_names.len() < 2 {
return;
}
while dir_names.len() > keep_num as usize {
let mut new_min = dir_names[0].clone();
let mut index = 0;
for (i, item) in dir_names.iter().enumerate().skip(1) {
if *item < new_min {
new_min = item.clone();
index = i;
}
}
dir_names.remove(index);
remove_dir_all(directory.join(new_min)).is_ok();
}
}
}
}