@@ -3,6 +3,7 @@ use crate::cli_helpers::{SizeFilter, TimeFilter};
33use crate :: glob_to_regex;
44use crate :: { DirEntry , FileType , SearchConfigError } ;
55use core:: num:: NonZeroU32 ;
6+ use core:: ops:: Deref ;
67use core:: time:: Duration ;
78use regex:: bytes:: { Regex , RegexBuilder } ;
89use std:: time:: UNIX_EPOCH ;
@@ -76,45 +77,47 @@ impl FileTypeFilter {
7677 }
7778 }
7879
79- /// Parses a character into a `FileTypeFilter`
80- ///
81- /// This method converts a single character into the corresponding file type filter,
82- /// which is useful for parsing command-line arguments or configuration files.
83- ///
84- /// # Parameters
85- /// - `c`: The character to parse into a file type filter
86- ///
87- /// # Returns
88- /// - `Ok(FileTypeFilter)` if the character represents a valid file type
89- /// - `Err(String)` with an error message if the character is invalid
90- ///
91- /// # Supported Characters
92- /// - `'d'` - Directory
93- /// - `'u'` - Unknown file type
94- /// - `'l'` - Symbolic link
95- /// - `'f'` - Regular file
96- /// - `'p'` - Named pipe (FIFO)
97- /// - `'c'` - Character device
98- /// - `'b'` - Block device
99- /// - `'s'` - Socket
100- /// - `'e'` - Empty file
101- /// - `'x'` - Executable file
102- ///
103- /// # Examples
104- /// ```
105- /// # use fdf::FileTypeFilter;
106- /// assert!(FileTypeFilter::from_char('d').is_ok());
107- /// assert!(FileTypeFilter::from_char('f').is_ok());
108- /// assert!(FileTypeFilter::from_char('z').is_err()); // Invalid character
109- ///
110- /// let filter = FileTypeFilter::from_char('l').unwrap();
111- /// assert!(matches!(filter, FileTypeFilter::Symlink));
112- /// ```
113- ///
114- /// # Errors
115- /// Returns an error if the character does not correspond to any known file type.
116- /// The error message includes the invalid character and suggests using `--help`
117- /// to see valid types.
80+ /**
81+ Parses a character into a `FileTypeFilter`
82+
83+ This method converts a single character into the corresponding file type filter,
84+ which is useful for parsing command-line arguments or configuration files.
85+
86+ # Parameters
87+ - `c`: The character to parse into a file type filter
88+
89+ # Returns
90+ - `Ok(FileTypeFilter)` if the character represents a valid file type
91+ - `Err(String)` with an error message if the character is invalid
92+
93+ # Supported Characters
94+ - `'d'` - Directory
95+ - `'u'` - Unknown file type
96+ - `'l'` - Symbolic link
97+ - `'f'` - Regular file
98+ - `'p'` - Named pipe (FIFO)
99+ - `'c'` - Character device
100+ - `'b'` - Block device
101+ - `'s'` - Socket
102+ - `'e'` - Empty file
103+ - `'x'` - Executable file
104+
105+ # Examples
106+ ```
107+ # use fdf::FileTypeFilter;
108+ assert!(FileTypeFilter::from_char('d').is_ok());
109+ assert!(FileTypeFilter::from_char('f').is_ok());
110+ assert!(FileTypeFilter::from_char('z').is_err()); // Invalid character
111+
112+ let filter = FileTypeFilter::from_char('l').unwrap();
113+ assert!(matches!(filter, FileTypeFilter::Symlink));
114+ ```
115+
116+ # Errors
117+ Returns an error if the character does not correspond to any known file type.
118+ The error message includes the invalid character and suggests using `--help`
119+ to see valid types.
120+ */
118121 pub fn from_char ( c : char ) -> core:: result:: Result < Self , String > {
119122 match c {
120123 'd' => Ok ( Self :: Directory ) ,
@@ -249,7 +252,7 @@ impl SearchConfig {
249252 /// Checks for extension match via memchr
250253 pub fn matches_extension < S > ( & self , entry : & S ) -> bool
251254 where
252- S : core :: ops :: Deref < Target = [ u8 ] > ,
255+ S : Deref < Target = [ u8 ] > ,
253256 {
254257 debug_assert ! (
255258 !entry. contains( & b'/' ) ,
@@ -267,10 +270,12 @@ impl SearchConfig {
267270 reason = "Only checking regular files"
268271 ) ]
269272 #[ allow( clippy:: cast_sign_loss) ] //signloss dont matter here
270- /// Applies the configured size filter to a directory entry, if any.
271- /// For regular files the size is checked directly.
272- /// For symlinks, the target is resolved first and then checked if it is a regular file.
273- /// Other file types are ignored.
273+ /**
274+ Applies the configured size filter to a directory entry, if any.
275+ For regular files the size is checked directly.
276+ For symlinks, the target is resolved first and then checked if it is a regular file.
277+ Other file types are ignored.
278+ */
274279 pub fn matches_size ( & self , entry : & DirEntry ) -> bool {
275280 let Some ( filter_size) = self . size_filter else {
276281 return true ; // No filter means always match
@@ -281,17 +286,10 @@ impl SearchConfig {
281286 . file_size ( )
282287 . ok ( )
283288 . is_some_and ( |sz| filter_size. is_within_size ( sz as _ ) ) ,
284-
285- FileType :: Symlink => {
286- if let Ok ( path) = entry. to_full_path ( ) {
287- if path. is_regular_file ( ) {
288- if let Ok ( sz) = entry. file_size ( ) {
289- return filter_size. is_within_size ( sz as _ ) ;
290- }
291- }
292- }
293- false
294- }
289+ // Resolve to full path first, this basically avoids broken symlinks
290+ FileType :: Symlink => entry. to_full_path_with_stat ( ) . is_ok_and ( |( path, statted) | {
291+ path. is_regular_file ( ) && filter_size. is_within_size ( statted. st_size as _ )
292+ } ) ,
295293
296294 _ => false ,
297295 }
@@ -344,8 +342,6 @@ impl SearchConfig {
344342 #[ expect( clippy:: indexing_slicing, reason = "used for debug assert" ) ]
345343 /// Checks if the path or file name matches the regex filter
346344 /// If `full_path` is false, only checks the filename
347- ///
348- /// Use a branchless trick to do indexing
349345 pub fn matches_path ( & self , dir : & DirEntry , full_path : bool ) -> bool {
350346 debug_assert ! (
351347 !dir. file_name( ) . contains( & b'/' ) ,
0 commit comments