use crate::error::{Error, ErrorKind, Result};
use std;
use std::fs::{remove_file, File};
use std::io::{Read, Write};
use std::path::Path;
pub struct CopyOptions {
pub overwrite: bool,
pub skip_exist: bool,
pub buffer_size: usize,
}
impl CopyOptions {
pub fn new() -> CopyOptions {
CopyOptions {
overwrite: false,
skip_exist: false,
buffer_size: 64000, }
}
pub fn overwrite(mut self, overwrite: bool) -> Self {
self.overwrite = overwrite;
self
}
pub fn skip_exist(mut self, skip_exist: bool) -> Self {
self.skip_exist = skip_exist;
self
}
pub fn buffer_size(mut self, buffer_size: usize) -> Self {
self.buffer_size = buffer_size;
self
}
}
impl Default for CopyOptions {
fn default() -> Self {
CopyOptions::new()
}
}
pub struct TransitProcess {
pub copied_bytes: u64,
pub total_bytes: u64,
}
pub fn copy<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let from = from.as_ref();
if !from.exists() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
err!(&msg, ErrorKind::NotFound);
}
err!(
"Path does not exist or you don't have access!",
ErrorKind::NotFound
);
}
if !from.is_file() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" is not a file!", msg);
err!(&msg, ErrorKind::InvalidFile);
}
err!("Path is not a file!", ErrorKind::InvalidFile);
}
if !options.overwrite && to.as_ref().exists() {
if options.skip_exist {
return Ok(0);
}
if let Some(msg) = to.as_ref().to_str() {
let msg = format!("Path \"{}\" exists", msg);
err!(&msg, ErrorKind::AlreadyExists);
}
}
Ok(std::fs::copy(from, to)?)
}
pub fn copy_with_progress<P, Q, F>(
from: P,
to: Q,
options: &CopyOptions,
mut progress_handler: F,
) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
F: FnMut(TransitProcess),
{
let from = from.as_ref();
if !from.exists() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
err!(&msg, ErrorKind::NotFound);
}
err!(
"Path does not exist or you don't have access!",
ErrorKind::NotFound
);
}
if !from.is_file() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" is not a file!", msg);
err!(&msg, ErrorKind::InvalidFile);
}
err!("Path is not a file!", ErrorKind::InvalidFile);
}
if !options.overwrite && to.as_ref().exists() {
if options.skip_exist {
return Ok(0);
}
if let Some(msg) = to.as_ref().to_str() {
let msg = format!("Path \"{}\" exists", msg);
err!(&msg, ErrorKind::AlreadyExists);
}
}
let mut file_from = File::open(from)?;
let mut buf = vec![0; options.buffer_size];
let file_size = file_from.metadata()?.len();
let mut copied_bytes: u64 = 0;
let mut file_to = File::create(to)?;
while !buf.is_empty() {
match file_from.read(&mut buf) {
Ok(0) => break,
Ok(n) => {
let written_bytes = file_to.write(&buf[..n])?;
if written_bytes != n {
err!("Couldn't write the whole buffer to file", ErrorKind::Other);
}
copied_bytes += n as u64;
let data = TransitProcess {
copied_bytes,
total_bytes: file_size,
};
progress_handler(data);
}
Err(ref e) if e.kind() == ::std::io::ErrorKind::Interrupted => {}
Err(e) => return Err(::std::convert::From::from(e)),
}
}
Ok(file_size)
}
pub fn move_file<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let mut is_remove = true;
if options.skip_exist && to.as_ref().exists() && !options.overwrite {
is_remove = false;
}
let result = copy(&from, to, options)?;
if is_remove {
remove(from)?;
}
Ok(result)
}
pub fn move_file_with_progress<P, Q, F>(
from: P,
to: Q,
options: &CopyOptions,
progress_handler: F,
) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
F: FnMut(TransitProcess),
{
let mut is_remove = true;
if options.skip_exist && to.as_ref().exists() && !options.overwrite {
is_remove = false;
}
let result = copy_with_progress(&from, to, options, progress_handler)?;
if is_remove {
remove(from)?;
}
Ok(result)
}
pub fn remove<P>(path: P) -> Result<()>
where
P: AsRef<Path>,
{
if path.as_ref().exists() {
Ok(remove_file(path)?)
} else {
Ok(())
}
}
pub fn read_to_string<P>(path: P) -> Result<String>
where
P: AsRef<Path>,
{
let path = path.as_ref();
if path.exists() && !path.is_file() {
if let Some(msg) = path.to_str() {
let msg = format!("Path \"{}\" is not a file!", msg);
err!(&msg, ErrorKind::InvalidFile);
}
err!("Path is not a file!", ErrorKind::InvalidFile);
}
let mut file = File::open(path)?;
let mut result = String::new();
file.read_to_string(&mut result)?;
Ok(result)
}
pub fn write_all<P>(path: P, content: &str) -> Result<()>
where
P: AsRef<Path>,
{
let path = path.as_ref();
if path.exists() && !path.is_file() {
if let Some(msg) = path.to_str() {
let msg = format!("Path \"{}\" is not a file!", msg);
err!(&msg, ErrorKind::InvalidFile);
}
err!("Path is not a file!", ErrorKind::InvalidFile);
}
let mut f = File::create(path)?;
Ok(f.write_all(content.as_bytes())?)
}