use std::io::ErrorKind;
use std::path::Path;
use std::path::PathBuf;
use std::time::Duration;
use sys_traits::FsCreateDirAll;
use sys_traits::FsDirEntry;
use sys_traits::FsHardLink;
use sys_traits::FsReadDir;
use sys_traits::FsRemoveFile;
use sys_traits::ThreadSleep;
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum HardLinkDirRecursiveError {
#[class(inherit)]
#[error(transparent)]
Io(#[from] std::io::Error),
#[class(inherit)]
#[error("Creating {path}")]
Creating {
path: PathBuf,
#[source]
#[inherit]
source: std::io::Error,
},
#[class(inherit)]
#[error("Creating {path}")]
Reading {
path: PathBuf,
#[source]
#[inherit]
source: std::io::Error,
},
#[class(inherit)]
#[error("Dir {from} to {to}")]
Dir {
from: PathBuf,
to: PathBuf,
#[source]
#[inherit]
source: Box<Self>,
},
#[class(inherit)]
#[error("Removing file to hard link {from} to {to}")]
RemoveFileToHardLink {
from: PathBuf,
to: PathBuf,
#[source]
#[inherit]
source: std::io::Error,
},
#[class(inherit)]
#[error("Hard linking {from} to {to}")]
HardLinking {
from: PathBuf,
to: PathBuf,
#[source]
#[inherit]
source: std::io::Error,
},
}
pub fn hard_link_dir_recursive<
TSys: FsCreateDirAll + FsHardLink + FsReadDir + FsRemoveFile + ThreadSleep,
>(
sys: &TSys,
from: &Path,
to: &Path,
) -> Result<(), HardLinkDirRecursiveError> {
sys.fs_create_dir_all(to).map_err(|source| {
HardLinkDirRecursiveError::Creating {
path: to.to_path_buf(),
source,
}
})?;
let read_dir = sys.fs_read_dir(from).map_err(|source| {
HardLinkDirRecursiveError::Reading {
path: from.to_path_buf(),
source,
}
})?;
for entry in read_dir {
let entry = entry?;
let file_type = entry.file_type()?;
let new_from = from.join(entry.file_name());
let new_to = to.join(entry.file_name());
if file_type.is_dir() {
hard_link_dir_recursive(sys, &new_from, &new_to).map_err(|source| {
HardLinkDirRecursiveError::Dir {
from: new_from.to_path_buf(),
to: new_to.to_path_buf(),
source: Box::new(source),
}
})?;
} else if file_type.is_file() {
if let Err(err) = sys.fs_hard_link(&new_from, &new_to) {
if err.kind() == ErrorKind::AlreadyExists {
if let Err(err) = sys.fs_remove_file(&new_to) {
if err.kind() == ErrorKind::NotFound {
sys.thread_sleep(Duration::from_millis(10));
} else {
return Err(HardLinkDirRecursiveError::RemoveFileToHardLink {
from: new_from.to_path_buf(),
to: new_to.to_path_buf(),
source: err,
});
}
}
if let Err(err) = sys.fs_hard_link(&new_from, &new_to) {
if err.kind() == ErrorKind::AlreadyExists {
sys.thread_sleep(Duration::from_millis(10));
} else {
return Err(HardLinkDirRecursiveError::HardLinking {
from: new_from.to_path_buf(),
to: new_to.to_path_buf(),
source: err,
});
}
}
} else {
return Err(HardLinkDirRecursiveError::HardLinking {
from: new_from.to_path_buf(),
to: new_to.to_path_buf(),
source: err,
});
}
}
}
}
Ok(())
}