refined exclusion rules
parent
8523c396c5
commit
0af9b732ab
35
src/args.rs
35
src/args.rs
|
@ -3,7 +3,7 @@ use glob::Pattern;
|
||||||
|
|
||||||
use crate::unit::Unit;
|
use crate::unit::Unit;
|
||||||
|
|
||||||
use std::fs::Metadata;
|
use std::fs::{symlink_metadata, Metadata};
|
||||||
use std::path::{Component, Path};
|
use std::path::{Component, Path};
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
use std::iter::once_with;
|
use std::iter::once_with;
|
||||||
|
@ -90,7 +90,6 @@ pub struct Args {
|
||||||
#[arg(
|
#[arg(
|
||||||
short='X', long,
|
short='X', long,
|
||||||
help = "exclude from search and printing",
|
help = "exclude from search and printing",
|
||||||
default_values_t = once_with(|| Pattern::new("").unwrap()),
|
|
||||||
value_parser = parse_glob,
|
value_parser = parse_glob,
|
||||||
value_delimiter = ',',
|
value_delimiter = ',',
|
||||||
action = ArgAction::Append,
|
action = ArgAction::Append,
|
||||||
|
@ -115,6 +114,9 @@ pub struct Args {
|
||||||
path: Vec<String>,
|
path: Vec<String>,
|
||||||
}
|
}
|
||||||
impl Args {
|
impl Args {
|
||||||
|
/// utility method to chuck default values on the end
|
||||||
|
/// it feels like I should be able to do this with
|
||||||
|
/// clever `clap` macros but I don't know how
|
||||||
pub fn post_process(mut self) -> Self {
|
pub fn post_process(mut self) -> Self {
|
||||||
if self.base_two {
|
if self.base_two {
|
||||||
self.unit = Unit::Kibi;
|
self.unit = Unit::Kibi;
|
||||||
|
@ -134,7 +136,14 @@ impl Args {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_exclude(&self, path: &Path, file: &Metadata) -> bool {
|
pub fn should_exclude(&self, path: &Path, file: &Metadata) -> bool {
|
||||||
if !self.follow_links && file.is_symlink() {
|
if !self.follow_links
|
||||||
|
&& file.is_symlink()
|
||||||
|
// `.` counts as a symlink. excluding . is usually not
|
||||||
|
// useful, so even if this is a symlink when we're not
|
||||||
|
// supposed to be following them, if this opens with a
|
||||||
|
// CurDir component then we shouldn't exclude
|
||||||
|
&& !matches!(path.components().nth(0), Some(Component::CurDir))
|
||||||
|
{
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,21 +188,31 @@ impl Args {
|
||||||
|
|
||||||
fn validate_path(s: &str) -> Result<String, String> {
|
fn validate_path(s: &str) -> Result<String, String> {
|
||||||
// try to access it's metadata, since that is what is used
|
// try to access it's metadata, since that is what is used
|
||||||
// to get its length
|
// to get its length. using symlink because that's what's
|
||||||
std::fs::metadata(s)
|
// used in the actual program
|
||||||
|
symlink_metadata(s)
|
||||||
.map(|_| s.to_string())
|
.map(|_| s.to_string())
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_glob(s: &str) -> Result<Pattern, String> {
|
fn parse_glob(s: &str) -> Result<Pattern, String> {
|
||||||
Pattern::new(s).map_err(|_| format!("invalid glob: {s}"))
|
Pattern::new(s).map_err(|globerr| format!("invalid glob \"{s}\": {}", globerr.msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn any_pattern_matches_any_component(patterns: &[Pattern], path: &Path) -> bool {
|
fn any_pattern_matches_any_component(patterns: &[Pattern], path: &Path) -> bool {
|
||||||
for pat in patterns {
|
for pat in patterns {
|
||||||
for cmp in path.components() {
|
for cmp in path.components() {
|
||||||
let Component::Normal(cmp) = cmp else { continue };
|
let Component::Normal(cmp) = cmp else {
|
||||||
let Some(s) = cmp.to_str() else { continue };
|
// this seems wacky, but only normal components
|
||||||
|
// are useful
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
let Some(s) = cmp.to_str() else {
|
||||||
|
// this is a code smell
|
||||||
|
// I don't believe it, but I can't think
|
||||||
|
// of anything worthwhile to do
|
||||||
|
continue
|
||||||
|
};
|
||||||
if pat.matches(s) {
|
if pat.matches(s) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,16 @@ impl Directory {
|
||||||
|
|
||||||
pub fn new< P: AsRef<Path> >(path: P, args: &Args) -> Result<Option<Self>> {
|
pub fn new< P: AsRef<Path> >(path: P, args: &Args) -> Result<Option<Self>> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let name = path.file_name()
|
// NOTE: I go back and forth on canonicalize()ing all the time.
|
||||||
|
// I feel like it changes every commit. The performance loss seems
|
||||||
|
// to be negligible, even when I do crazy things like `hb -p /`, which
|
||||||
|
// is the most I can currently do.
|
||||||
|
let name = match (path.canonicalize(), args.persistant()) {
|
||||||
|
(Ok(path), _) => path,
|
||||||
|
(Err(_), true) => return Ok(None),
|
||||||
|
(Err(e), false) => return Err(e.into()),
|
||||||
|
}
|
||||||
|
.file_name()
|
||||||
.map_or_else(|| OsString::from("/"), ToOwned::to_owned)
|
.map_or_else(|| OsString::from("/"), ToOwned::to_owned)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue