mirror of
https://github.com/sharkdp/fd
synced 2024-10-14 03:32:31 +00:00
Initial impl of size constraints.
This adds find-style size constraints with + or - indicating greater or less than, a numerical size, and a unit
This commit is contained in:
parent
fea85dbfb3
commit
0207c1371e
28
src/app.rs
28
src/app.rs
|
@ -5,7 +5,6 @@
|
|||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use clap::{App, AppSettings, Arg};
|
||||
|
@ -16,10 +15,10 @@ struct Help {
|
|||
}
|
||||
|
||||
macro_rules! doc {
|
||||
($map:expr, $name:expr, $short:expr) => {
|
||||
($map: expr, $name: expr, $short: expr) => {
|
||||
doc!($map, $name, $short, $short)
|
||||
};
|
||||
($map:expr, $name:expr, $short:expr, $long:expr) => {
|
||||
($map: expr, $name: expr, $short: expr, $long: expr) => {
|
||||
$map.insert(
|
||||
$name,
|
||||
Help {
|
||||
|
@ -146,6 +145,15 @@ pub fn build_app() -> App<'static, 'static> {
|
|||
.takes_value(true)
|
||||
.value_name("num"),
|
||||
)
|
||||
.arg(
|
||||
arg("size")
|
||||
.long("size")
|
||||
.short("S")
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.allow_hyphen_values(true)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
arg("max-buffer-time")
|
||||
.long("max-buffer-time")
|
||||
|
@ -254,6 +262,18 @@ fn usage() -> HashMap<&'static str, Help> {
|
|||
doc!(h, "rg-alias-hidden-ignore"
|
||||
, "Alias for no-ignore and/or hidden"
|
||||
, "Alias for no-ignore ('u') and no-ignore and hidden ('uu')");
|
||||
|
||||
doc!(h, "size"
|
||||
, "Limit results based on the size of files."
|
||||
, "Limit results based on the size of files using the format <+-><NUM><UN>.\n \
|
||||
'+': file size must be greater than this\n \
|
||||
'-': file size must be less than this\n \
|
||||
'NUM': The numeric size (e.g. 500)\n \
|
||||
'UN': The units for NUM.\n\
|
||||
Allowed unit values:\n \
|
||||
'b' or 'B': bytes\n \
|
||||
'k' or 'K': kilobytes\n \
|
||||
'm' or 'M': megabytes\n \
|
||||
'g' or 'G': gigabytes\n \
|
||||
't' or 'T': terabytes");
|
||||
h
|
||||
}
|
||||
|
|
|
@ -14,10 +14,14 @@ use std::time;
|
|||
|
||||
use exec::CommandTemplate;
|
||||
use lscolors::LsColors;
|
||||
use regex::RegexSet;
|
||||
use regex::{Regex, RegexSet};
|
||||
use regex_syntax::hir::Hir;
|
||||
use regex_syntax::Parser;
|
||||
|
||||
lazy_static! {
|
||||
static ref SIZE_CAPTURES: Regex = { Regex::new(r"^(\+|-)(\d+)([a-zA-Z]{1,2})$").unwrap() };
|
||||
}
|
||||
|
||||
/// Whether or not to show
|
||||
pub struct FileTypes {
|
||||
pub files: bool,
|
||||
|
@ -37,6 +41,58 @@ impl Default for FileTypes {
|
|||
}
|
||||
}
|
||||
|
||||
enum SizeLimitType {
|
||||
Max,
|
||||
Min,
|
||||
}
|
||||
|
||||
pub struct SizeFilter {
|
||||
size: u64,
|
||||
limit_type: SizeLimitType,
|
||||
}
|
||||
|
||||
impl SizeFilter {
|
||||
pub fn is_within(&self, size: u64) -> bool {
|
||||
match self.limit_type {
|
||||
SizeLimitType::Max => size <= self.size,
|
||||
SizeLimitType::Min => size >= self.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const KILO: u64 = 1024;
|
||||
const MEGA: u64 = KILO * 1024;
|
||||
const GIGA: u64 = MEGA * 1024;
|
||||
const TERA: u64 = GIGA * 1024;
|
||||
|
||||
impl<'a> From<&'a str> for SizeFilter {
|
||||
/// Create the `SizeFilter` from the given `&str`.
|
||||
/// It is imperative that the incoming value has been validated for
|
||||
/// proper format.
|
||||
fn from(s: &str) -> Self {
|
||||
let captures = SIZE_CAPTURES.captures(s).unwrap();
|
||||
let limit = match captures.get(1).map_or("+", |m| m.as_str()) {
|
||||
"+" => SizeLimitType::Min,
|
||||
_ => SizeLimitType::Max,
|
||||
};
|
||||
|
||||
let quantity = captures.get(2).unwrap().as_str().parse::<u64>().unwrap();
|
||||
|
||||
let multiplier = match &captures.get(3).map_or("m", |m| m.as_str()).to_lowercase()[..] {
|
||||
"k" => KILO,
|
||||
"m" => MEGA,
|
||||
"g" => GIGA,
|
||||
"t" => TERA,
|
||||
_ => 1, // Any we don't understand we'll just say the number of bytes
|
||||
};
|
||||
|
||||
SizeFilter {
|
||||
size: quantity * multiplier,
|
||||
limit_type: limit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration options for *fd*.
|
||||
pub struct FdOptions {
|
||||
/// Whether the search is case-sensitive or case-insensitive.
|
||||
|
@ -96,6 +152,9 @@ pub struct FdOptions {
|
|||
|
||||
/// A list of custom ignore files.
|
||||
pub ignore_files: Vec<PathBuf>,
|
||||
|
||||
/// The given constraints on the size of returned files
|
||||
pub size_constraints: Vec<SizeFilter>,
|
||||
}
|
||||
|
||||
/// Print error message to stderr and exit with status `1`.
|
||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -34,12 +34,17 @@ use std::sync::Arc;
|
|||
use std::time;
|
||||
|
||||
use atty::Stream;
|
||||
use regex::{RegexBuilder, RegexSetBuilder};
|
||||
use regex::{RegexBuilder, RegexSetBuilder, Regex};
|
||||
|
||||
use exec::CommandTemplate;
|
||||
use internal::{error, pattern_has_uppercase_char, transform_args_with_exec, FdOptions, FileTypes};
|
||||
use internal::{error, pattern_has_uppercase_char, transform_args_with_exec, FdOptions, FileTypes,
|
||||
SizeFilter};
|
||||
use lscolors::LsColors;
|
||||
|
||||
lazy_static! {
|
||||
static ref VALIDATE_SIZE: Regex = { Regex::new(r"^[\+-]{1}\d+[bBkKmMgGTt]{1,2}$").unwrap() };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let checked_args = transform_args_with_exec(env::args_os());
|
||||
let matches = app::build_app().get_matches_from(checked_args);
|
||||
|
@ -132,6 +137,16 @@ fn main() {
|
|||
|
||||
let command = matches.values_of("exec").map(CommandTemplate::new);
|
||||
|
||||
let size_limits: Vec<SizeFilter> = matches
|
||||
.values_of("size")
|
||||
.map(|v| v.map(|sf| {
|
||||
if !VALIDATE_SIZE.is_match(sf) {
|
||||
error(&format!("Error: {} is not a valid size constraint.", sf));
|
||||
}
|
||||
sf.into()
|
||||
}).collect())
|
||||
.unwrap_or_else(|| vec![]);
|
||||
|
||||
let config = FdOptions {
|
||||
case_sensitive,
|
||||
search_full_path: matches.is_present("full-path"),
|
||||
|
@ -195,6 +210,7 @@ fn main() {
|
|||
.values_of("ignore-file")
|
||||
.map(|vs| vs.map(PathBuf::from).collect())
|
||||
.unwrap_or_else(|| vec![]),
|
||||
size_constraints: size_limits,
|
||||
};
|
||||
|
||||
match RegexBuilder::new(&pattern_regex)
|
||||
|
|
10
src/walk.rs
10
src/walk.rs
|
@ -248,6 +248,16 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<FdOptions>) {
|
|||
}
|
||||
}
|
||||
|
||||
// Filter out unwanted sizes if it is a file and we have been given size constraints.
|
||||
if entry_path.is_file() && config.size_constraints.len() > 0 {
|
||||
if let Ok(metadata) = entry_path.metadata() {
|
||||
let file_size = metadata.len();
|
||||
if config.size_constraints.iter().any(|sc| !sc.is_within(file_size)) {
|
||||
return ignore::WalkState::Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let search_str_o = if config.search_full_path {
|
||||
match fshelper::path_absolute_form(entry_path) {
|
||||
Ok(path_abs_buf) => Some(path_abs_buf.to_string_lossy().into_owned().into()),
|
||||
|
|
Loading…
Reference in a new issue