mod private
{
#[ cfg( feature = "enabled" ) ]
extern crate std;
#[ cfg( feature = "enabled" ) ]
use std::path::{ Path, PathBuf };
#[ cfg( feature = "enabled" ) ]
use std::io;
#[ cfg( feature = "enabled" ) ]
#[ inline ]
pub fn traverse_upward< T, F >( start_dir: &Path, predicate: F, max_depth: usize ) -> Option< T >
where
F: Fn( &Path ) -> Option< T >
{
let mut current = start_dir.to_path_buf();
for _ in 0..max_depth
{
if let Some( result ) = predicate( ¤t )
{
return Some( result );
}
if let Some( parent ) = current.parent()
{
current = parent.to_path_buf();
}
else
{
break;
}
}
None
}
#[ cfg( feature = "enabled" ) ]
pub fn collect_files_in_ancestors< F >(
target: &Path,
predicate: F,
max_depth: Option< usize >,
deduplicate: bool
) -> io::Result< Vec< PathBuf > >
where
F: Fn( &Path ) -> bool
{
use std::collections::HashSet;
if !target.exists()
{
return Err( io::Error::new(
io::ErrorKind::NotFound,
format!( "Directory does not exist: {}", target.display() )
) );
}
let target = target.canonicalize()?;
let mut hierarchy = Vec::new();
let mut current = target.as_path();
let mut depth_count = 0;
loop
{
hierarchy.push( current.to_path_buf() );
depth_count += 1;
if let Some( max ) = max_depth
{
if depth_count >= max
{
break;
}
}
match current.parent()
{
Some( parent ) if parent != current => current = parent,
_ => break,
}
}
hierarchy.reverse();
let mut results = Vec::new();
let mut seen_paths: HashSet< PathBuf > = HashSet::new();
for dir in &hierarchy
{
if let Ok( entries ) = std::fs::read_dir( dir )
{
for entry in entries.flatten()
{
let path = entry.path();
if path.is_file() && predicate( &path )
{
if deduplicate
{
if let Ok( canonical ) = path.canonicalize()
{
if seen_paths.insert( canonical )
{
results.push( path );
}
}
}
else
{
results.push( path );
}
}
}
}
}
Ok( results )
}
#[ cfg( feature = "enabled" ) ]
#[ inline ]
#[ must_use ]
pub fn file_upward_find( start: &Path, filename: &str, max_depth: usize ) -> Option< PathBuf >
{
traverse_upward(
start,
| dir |
{
let candidate = dir.join( filename );
if candidate.exists() && candidate.is_file()
{
Some( candidate )
}
else
{
None
}
},
max_depth
)
}
#[ cfg( feature = "enabled" ) ]
#[ inline ]
#[ must_use ]
pub fn dir_upward_find( start: &Path, dirname: &str, max_depth: usize ) -> Option< PathBuf >
{
traverse_upward(
start,
| dir |
{
let candidate = dir.join( dirname );
if candidate.exists() && candidate.is_dir()
{
Some( candidate )
}
else
{
None
}
},
max_depth
)
}
#[ cfg( feature = "enabled" ) ]
#[ inline ]
pub fn matching_upward_find< F >( start: &Path, predicate: F, max_depth: usize ) -> Option< PathBuf >
where
F: Fn( &Path ) -> bool
{
traverse_upward(
start,
| dir |
{
if predicate( dir )
{
Some( dir.to_path_buf() )
}
else
{
None
}
},
max_depth
)
}
}
#[ allow( unused_imports ) ]
pub mod own
{
use super :: *;
#[ doc( inline ) ]
pub use orphan :: *;
}
#[ doc( inline ) ]
#[ allow( unused_imports ) ]
pub use own :: *;
#[ allow( unused_imports ) ]
pub mod orphan
{
use super :: *;
#[ doc( inline ) ]
pub use exposed :: *;
}
#[ allow( unused_imports ) ]
pub mod exposed
{
use super :: *;
#[ doc( inline ) ]
pub use prelude :: *;
#[ cfg( feature = "enabled" ) ]
#[ doc( inline ) ]
pub use super::private::
{
traverse_upward,
collect_files_in_ancestors,
file_upward_find,
dir_upward_find,
matching_upward_find,
};
}
#[ allow( unused_imports ) ]
pub mod prelude
{
use super :: *;
}