use std::path::{Path, PathBuf};
use crate::parser::Pickable;
mod rule;
pub use rule::*;
impl Pickable for Vec<PathBuf> {
type Output = Vec<PathBuf>;
fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
let raw: Vec<String> = args.pick_arguments(flag);
let paths: Vec<PathBuf> = raw.into_iter().map(PathBuf::from).collect();
Some(paths)
}
}
impl Pickable for PathBuf {
type Output = PathBuf;
fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
let raw: String = args.pick_argument(flag)?;
let path: PathBuf = PathBuf::from(raw);
Some(path)
}
}
pub trait PathsChecker {
fn is_all_passed(&self, rule: &PathCheckRule) -> bool
where
Self: Into<Vec<PathBuf>> + Clone,
{
check_paths(self.clone(), rule).is_ok()
}
fn classify(self, rule: &PathCheckRule) -> (Vec<PathBuf>, Vec<PathBuf>)
where
Self: Into<Vec<PathBuf>>,
{
let paths = self.into();
let mut passed = Vec::new();
let mut stripped = Vec::new();
for path in paths {
if check_path(&path, rule).is_ok() {
passed.push(path);
} else {
stripped.push(path);
}
}
(passed, stripped)
}
fn passed(self, rule: &PathCheckRule) -> Vec<PathBuf>
where
Self: Into<Vec<PathBuf>>,
{
self.classify(rule).0
}
fn stripped(self, rule: &PathCheckRule) -> Vec<PathBuf>
where
Self: Into<Vec<PathBuf>>,
{
self.classify(rule).1
}
}
pub trait PathChecker {
fn is_passed(&self, rule: &PathCheckRule) -> bool
where
Self: Into<PathBuf> + Clone,
{
check_path(self.clone(), rule).is_ok()
}
}
impl<T: Into<Vec<PathBuf>>> PathsChecker for T where T: Into<Vec<PathBuf>> {}
impl<T: Into<PathBuf>> PathChecker for T where T: Into<PathBuf> {}
fn check_paths(path: impl Into<Vec<PathBuf>>, rule: &PathCheckRule) -> Result<(), ()> {
let paths = path.into();
for p in paths.iter() {
check_exist(p, rule)?;
check_type(p, rule)?;
}
Ok(())
}
fn check_path(path: impl Into<PathBuf>, rule: &PathCheckRule) -> Result<(), ()> {
let p = path.into();
check_exist(&p, rule)?;
check_type(&p, rule)?;
Ok(())
}
fn check_exist(path: &Path, rule: &PathCheckRule) -> Result<(), ()> {
let Some(exist_check) = &rule.exist_check else {
return Ok(());
};
match exist_check {
PathExistCheck::Exists => bool_to_result(path.exists()),
PathExistCheck::NotExists => bool_to_result(!path.exists()),
}
}
fn check_type(path: &Path, rule: &PathCheckRule) -> Result<(), ()> {
let Some(type_check) = &rule.type_check else {
return Ok(());
};
let is_dir = path.is_dir();
let is_file = path.is_file();
let is_symlink = path.is_symlink();
if type_check.allow_dir && is_dir {
return Ok(());
}
if type_check.allow_file && is_file {
return Ok(());
}
if type_check.allow_symlink && is_symlink {
return Ok(());
}
Err(())
}
fn bool_to_result(b: bool) -> Result<(), ()> {
if b { Ok(()) } else { Err(()) }
}