use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::os::unix::ffi::OsStrExt;
use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
use anyhow::Result;
use percent_encoding::AsciiSet;
use walkdir::WalkDir;
const MASK: &AsciiSet = percent_encoding::CONTROLS;
pub fn into_absolute(path: impl AsRef<Path>) -> Result<PathBuf> {
let path = path.as_ref();
let mut new_path = if !path.is_absolute() {
env::current_dir()?
} else {
path.to_path_buf()
};
for component in path.components() {
match component {
Component::Prefix(_) => unimplemented!("windows lol"),
Component::RootDir => return Ok(path.to_path_buf()),
Component::CurDir => {}
Component::ParentDir => {
new_path.pop();
}
Component::Normal(s) => {
new_path.push(s);
}
}
}
Ok(new_path)
}
pub fn get_uid() -> u64 {
unsafe { libc::getuid().into() }
}
pub fn recursive_copy(
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
) -> Result<()> {
let src = src.as_ref();
let dst = dst.as_ref();
for entry in WalkDir::new(src)
.contents_first(false)
.follow_links(false)
.same_file_system(true)
{
let entry = entry?;
let path = entry.path();
let relative_path = path.strip_prefix(src)?;
if let None = relative_path.file_name() {
fs::create_dir(dst)?;
continue;
}
let target_name = dst.join(relative_path);
if path.is_dir() {
fs::create_dir(&target_name)?;
} else {
fs::copy(path, &target_name)?;
}
}
Ok(())
}
pub fn percent_encode(path: impl AsRef<Path>) -> String {
let path = path.as_ref();
path
.components()
.filter_map(|segment| {
match segment {
Component::RootDir => Some(String::new()),
Component::Prefix(_) => unreachable!("windows"),
Component::ParentDir => {
unreachable!("path should be normalized")
}
Component::CurDir => unreachable!("path should be normalized"),
Component::Normal(s) => {
Some(percent_encoding::percent_encode(s.as_bytes(), MASK).to_string())
}
}
})
.collect::<Vec<_>>()
.join(&MAIN_SEPARATOR.to_string())
}
pub(crate) fn concat_os_str_iter<'a>(ss: &Vec<&'a OsStr>) -> OsString {
let mut len = 0;
for s in ss {
len += s.len();
}
let mut buf = vec![0; len];
let mut c = 0;
for s in ss {
let segment = &mut buf[c..c + s.len()];
segment.copy_from_slice(s.as_bytes());
c += s.len();
}
OsStr::from_bytes(&buf).to_os_string()
}
#[macro_export]
macro_rules! concat_os_str {
($($str:expr),* $(,)?) => {
{
let _wtf = vec![
$($str.as_ref(),)*
];
crate::utils::concat_os_str_iter(&_wtf)
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_absolute() {
let cwd = env::current_dir().unwrap();
let path = PathBuf::from("..");
let parent = cwd.parent().unwrap();
assert_eq!(into_absolute(&path).unwrap(), parent);
let path2 = path.join(cwd.file_name().unwrap());
assert_eq!(into_absolute(&path2).unwrap(), cwd);
}
}