use crate::{Context, Result};
use std::env;
use std::path::{Path, PathBuf};
cfg_if! {
if #[cfg(unix)] {
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
}
}
const MAX_PERMISSIONS: u16 = 0x1FF;
pub fn search_backwards_for_from_pwd(files: Vec<&str>) -> (bool, PathBuf) {
let base = env::current_dir();
let base = match base {
Ok(p) => p,
Err(_e) => {
return (false, PathBuf::new());
}
};
search_backwards_for(files, &base)
}
pub fn search_backwards_for(files: Vec<&str>, base: &Path) -> (bool, PathBuf) {
let mut aborted = false;
let mut base = base.to_path_buf();
log_debug!(
"Searching backwards from '{}' for '{:?}'",
base.display(),
&files
);
while !files
.iter()
.fold(base.clone(), |acc, p| acc.join(p))
.is_file()
&& !aborted
{
if !base.pop() {
aborted = true;
}
}
if aborted {
log_debug!("Not found");
(false, PathBuf::new())
} else {
log_debug!("Found at '{}'", base.display());
(true, base)
}
}
pub fn search_backwards_for_first<T, F>(mut start_path: PathBuf, mut func: F) -> Result<Option<T>>
where
F: FnMut(&Path) -> Result<Option<T>>
{
if let Some(res) = func(&start_path)? {
return Ok(Some(res));
}
while start_path.pop() {
if let Some(res) = func(&start_path)? {
return Ok(Some(res));
}
}
Ok(None)
}
pub fn cd(dir: &Path) -> Result<()> {
env::set_current_dir(&dir).context(&format!("When cd'ing to '{}'", dir.display()))?;
Ok(())
}
pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> {
#[cfg(windows)]
{
if src.as_ref().is_dir() {
Ok(std::os::windows::fs::symlink_dir(src, dst)?)
} else {
Ok(std::os::windows::fs::symlink_file(src, dst)?)
}
}
#[cfg(unix)]
{
Ok(std::os::unix::fs::symlink(src, dst)?)
}
}
pub fn with_dir<T, F>(path: &Path, mut f: F) -> Result<T>
where
F: FnMut() -> Result<T>,
{
log_trace!("Changing directory to '{}'", path.display());
let orig = env::current_dir()?;
env::set_current_dir(path)?;
let result = f();
log_trace!("Restoring directory to '{}'", orig.display());
env::set_current_dir(&orig)?;
result
}
#[derive(Debug, Clone, PartialEq)]
pub enum FilePermissions {
Private,
Group,
GroupWritable,
PublicWithGroupWritable,
Public,
WorldWritable,
Custom(u16),
}
impl FilePermissions {
pub fn to_str(&self) -> String {
match self {
Self::Private => "private".to_string(),
Self::Group => "group".to_string(),
Self::GroupWritable => "group_writable".to_string(),
Self::PublicWithGroupWritable => "public_with_group_writable".to_string(),
Self::Public => "public".to_string(),
Self::WorldWritable => "world_writable".to_string(),
Self::Custom(perms) => format!("custom({:#05o})", perms),
}
}
pub fn to_i(&self) -> u16 {
match self {
Self::Private => 0o700,
Self::Group => 0o750,
Self::GroupWritable => 0o770,
Self::PublicWithGroupWritable => 0o775,
Self::Public => 0o755,
Self::WorldWritable => 0o777,
Self::Custom(perms) => *perms,
}
}
pub fn from_str(perms: &str) -> Result<Self> {
match perms.to_lowercase().as_str() {
"private" => Ok(Self::Private),
"group" => Ok(Self::Group),
"group_writable" => Ok(Self::GroupWritable),
"public_with_group_writable" => Ok(Self::PublicWithGroupWritable),
"public" => Ok(Self::Public),
"world_writable" => Ok(Self::WorldWritable),
_ => Err(error!("Cannot infer permissions from input '{}'", perms)),
}
}
pub fn from_i(perms: u16) -> Result<Self> {
match perms {
0o700 => Ok(Self::Private),
0o750 => Ok(Self::Group),
0o770 => Ok(Self::GroupWritable),
0o775 => Ok(Self::PublicWithGroupWritable),
0o755 => Ok(Self::Public),
0o777 => Ok(Self::WorldWritable),
_ => {
if perms > MAX_PERMISSIONS {
bail!(
"Given permissions {:#o} exceeds maximum supported Unix permissions {:#o}",
perms,
MAX_PERMISSIONS
)
} else {
Ok(Self::Custom(perms))
}
}
}
}
#[allow(unused_variables)]
pub fn apply_to(&self, path: &Path, warn_when_unsupported: bool) -> Result<()> {
cfg_if! {
if #[cfg(unix)] {
let f = File::open(path)?;
let m = f.metadata()?;
let mut permissions = m.permissions();
permissions.set_mode(self.to_i().into());
f.set_permissions(permissions)?;
Ok(())
} else {
let message = format!(
"Changing file permissions to {} is not supported on OS {}",
self.to_str(),
std::env::consts::OS
);
if warn_when_unsupported {
crate::LOGGER.warning(&message);
Ok(())
} else {
bail!("{}", message)
}
}
}
}
}