use once_cell::sync::Lazy;
use rayon::iter::ParallelBridge;
use rayon::iter::ParallelIterator;
use regex::Regex;
use regex::RegexBuilder;
use serde::Serialize;
use std::fs::OpenOptions;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use crate::error::Fallible;
#[inline]
pub fn ensure_dir<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<()> {
std::fs::create_dir_all(path.as_ref())
}
#[inline]
pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
remove_dir_all::remove_dir_all(path)
}
#[inline(always)]
pub fn empty_dir<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<()> {
let path = path.as_ref();
match path.exists() {
true => remove_dir_all::remove_dir_contents(path),
false => Ok(()),
}
}
pub fn walk_dir_json<P: AsRef<Path>>(path: P) -> io::Result<Vec<PathBuf>> {
let path = path.as_ref();
Ok(path
.read_dir()?
.par_bridge()
.filter_map(io::Result::ok)
.filter(|de| {
let path = de.path();
let name = path.file_name().unwrap().to_str().unwrap();
path.is_file() && name.ends_with(".json") && name != "package.json"
})
.map(|de| de.path())
.collect::<Vec<_>>())
}
#[inline]
pub fn filenamify<S: AsRef<str>>(filename: S) -> String {
static REGEX_REPLACE: Lazy<Regex> =
Lazy::new(|| RegexBuilder::new(r"[^\w.-]+").build().unwrap());
REGEX_REPLACE
.replace_all(filename.as_ref(), "_")
.into_owned()
}
pub fn write_json<P, D>(path: P, data: D) -> Fallible<()>
where
P: AsRef<Path>,
D: Serialize,
{
let path = path.as_ref();
ensure_dir(path.parent().unwrap())?;
let file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)?;
Ok(serde_json::to_writer_pretty(file, &data)?)
}
#[cfg(windows)]
pub fn remove_symlink<P: AsRef<Path>>(lnk: P) -> io::Result<()> {
let lnk = lnk.as_ref();
let metadata = lnk.symlink_metadata()?;
let mut permissions = metadata.permissions();
if permissions.readonly() {
#[allow(clippy::permissions_set_readonly_false)]
permissions.set_readonly(false);
std::fs::set_permissions(lnk, permissions)?;
}
if let Ok(target_metadata) = lnk.metadata() {
if target_metadata.file_type().is_dir() {
std::fs::remove_dir(lnk)
} else {
std::fs::remove_file(lnk)
}
} else {
std::fs::remove_file(lnk).or_else(|_| std::fs::remove_dir(lnk))
}
}
#[cfg(unix)]
#[inline]
pub fn remove_symlink<P: AsRef<Path>>(lnk: P) -> io::Result<()> {
std::fs::remove_file(lnk)
}
#[cfg(windows)]
pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, lnk: Q) -> io::Result<()> {
let src = src.as_ref();
let lnk = lnk.as_ref();
remove_symlink(lnk)?;
ensure_dir(lnk.parent().unwrap())?;
if std::os::windows::fs::symlink_dir(src, lnk).is_err() {
junction::create(src, lnk)
} else {
Ok(())
}
}
#[cfg(unix)]
#[inline]
pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, lnk: Q) -> io::Result<()> {
std::os::unix::fs::symlink(src, lnk)
}
#[cfg(windows)]
pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, lnk: Q) -> io::Result<()> {
let src = src.as_ref();
let lnk = lnk.as_ref();
remove_symlink(lnk)?;
ensure_dir(lnk.parent().unwrap())?;
if std::os::windows::fs::symlink_file(src, lnk).is_err() {
std::fs::hard_link(src, lnk)
} else {
Ok(())
}
}
#[cfg(unix)]
#[inline]
pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, lnk: Q) -> io::Result<()> {
std::os::unix::fs::symlink(src, lnk)
}
#[cfg(windows)]
pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, lnk: Q) -> io::Result<()> {
let src = src.as_ref();
let lnk = lnk.as_ref();
let metadata = src.metadata()?;
if metadata.file_type().is_dir() {
symlink_dir(src, lnk)
} else {
symlink_file(src, lnk)
}
}
#[cfg(unix)]
#[inline]
pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, lnk: Q) -> io::Result<()> {
std::os::unix::fs::symlink(src, lnk)
}