use std::path::{Path, PathBuf}; use crate::parser::Pickable; mod rule; pub use rule::*; impl Pickable for Vec { type Output = Vec; fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option { let raw: Vec = args.pick_arguments(flag); let paths: Vec = 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 { let raw: String = args.pick_argument(flag)?; let path: PathBuf = PathBuf::from(raw); Some(path) } } /// Provides path checking methods for [`Vec`] /// /// This trait automatically provides implementations for `Into>` pub trait PathsChecker { /// Check if all paths in the list satisfy the rule fn is_all_passed(&self, rule: &PathCheckRule) -> bool where Self: Into> + Clone, { check_paths(self.clone(), rule).is_ok() } /// Classify paths into (Passed, Stripped) /// /// Passed means paths that satisfy the rule, Stripped means paths that do not. fn classify(self, rule: &PathCheckRule) -> (Vec, Vec) where Self: Into>, { 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) } /// Return paths that satisfy the rule fn passed(self, rule: &PathCheckRule) -> Vec where Self: Into>, { self.classify(rule).0 } /// Return paths that do not satisfy the rule fn stripped(self, rule: &PathCheckRule) -> Vec where Self: Into>, { self.classify(rule).1 } } /// Provides path checking methods for [`PathBuf`] /// /// This trait automatically provides implementations for `Into` pub trait PathChecker { fn is_passed(&self, rule: &PathCheckRule) -> bool where Self: Into + Clone, { check_path(self.clone(), rule).is_ok() } } impl>> PathsChecker for T where T: Into> {} impl> PathChecker for T where T: Into {} fn check_paths(path: impl Into>, 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, 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(()) } }