Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate over std::fs::ReadDir and get only filenames from paths

Tags:

rust

I'm trying to figure out how to do a 'list Comprehension' with rust. I have a ReadDir iter that I want to map and only get the filenames of the paths. I'm thinking it would look something like:

// get current dir paths
let paths = fs::read_dir(&Path::new(
    &env::current_dir().unwrap())).unwrap();

// should contain only filenames
let files_names = paths.map(|&x| {
    match (*x).extensions() {
        Some(y) => y,
        None => //What do I do here? break?
};

I know my expression is likely horrendously broken, but is there a way to make this a single expression that I can bind file_names to?

like image 691
Syntactic Fructose Avatar asked Jul 04 '15 22:07

Syntactic Fructose


1 Answers

You'll probably want to use Path's file_name() method which yields the file name if it's a regular file, which is why it returns an Option.

I imagine you'd want to ignore the file names of things that aren't regular files, i.e. for which file_name() would return None, in which case you will probably want to leverage Iterator's filter_map method which is basically like filter and map put together, i.e. you're able to map (in this case the path -> file name) and filter by returning the mapped result as an Option, where None would signify that you want to filter that value out.

The annoying thing will be that you have to check each item returned by the ReadDir iterator, since that's a Result type, so you'll have to see if it's Ok. If you just want to ignore non-Ok (i.e. Err) entries in the directory (e.g. entries for which you don't have permission), you can simply convert it to an Option using Result's ok() method and integrate that in the filter_map.

You'll also have to attempt to create a String from the returned file_name(), since it may not necessarily be UTF-8. Files with non-UTF-8 names can simply be ignored (in this example) again using a combination of map and and_then.

Here's what it would look like if you make it ignore non-Ok directory entries and paths which aren't regular files (and thus return None on file_name()) as well as files whose file names are not UTF-8:

let paths = fs::read_dir(&Path::new(
  &env::current_dir().unwrap())).unwrap();

let names =
paths.filter_map(|entry| {
  entry.ok().and_then(|e|
    e.path().file_name()
    .and_then(|n| n.to_str().map(|s| String::from(s)))
  )
}).collect::<Vec<String>>();

playpen

If you're not very familiar with Rust's functional flow-control functions, e.g. map, and_then, etc. on Result and Option, then here's what it would look like expanded out, without ignoring errors and without doing error handling. I leave that up to you:

let paths = fs::read_dir(&Path::new(
  &env::current_dir().unwrap())).unwrap();

let names =
paths.map(|entry| {
  let entry = entry.unwrap();

  let entry_path = entry.path();

  let file_name = entry_path.file_name().unwrap();

  let file_name_as_str = file_name.to_str().unwrap();

  let file_name_as_string = String::from(file_name_as_str);

  file_name_as_string
}).collect::<Vec<String>>();

playpen

like image 78
Jorge Israel Peña Avatar answered Nov 01 '22 22:11

Jorge Israel Peña