Search code examples
rustfile-type

Check if a file is of a given type


How can I fix this function? I think that I have to convert the name to String but then I would have to convert it back to str to use ends_with. Should I use another function to do that?

use walkdir::{DirEntry};

fn is_filetype(entry: &DirEntry, _type: &str) -> bool {
    entry.file_name()
        .to_str()
        .to_lowercase()
        .map(|s| s.ends_with(_type))
        .unwrap_or(false)
}

error[E0599]: no method named `to_lowercase` found for enum `Option` in the current scope
  --> src/bin/parse_torrent.rs:45:10
   |
45 |         .to_lowercase()
   |          ^^^^^^^^^^^^ method not found in `Option<&str>`

Solution

  • The easiest way to fix the code provided in the question would probably be to use map_or like @ChayimFriedman suggests in the comments.

    fn is_filetype(entry: &DirEntry, _type: &str) -> bool {
        entry
            .file_name()
            .to_str()
            .map_or(false, |s| s.to_lowercase().ends_with(_type))
    }
    

    However if we expand this to also look at other approaches there are a few more options. In the past, one approach I used was to use the built in .extension() method.

    if path.extension().and_then(OsStr::to_str) == Some("txt") {
        do_the_thing();
    }
    

    However, we can probably do better. After a bit of tinkering, here is what I came up with.

    pub trait FileExtension {
        fn has_extension<S: AsRef<str>>(&self, extensions: &[S]) -> bool;
    }
    
    impl<P: AsRef<Path>> FileExtension for P {
        fn has_extension<S: AsRef<str>>(&self, extensions: &[S]) -> bool {
            if let Some(ref extension) = self.as_ref().extension().and_then(OsStr::to_str) {
                return extensions
                    .iter()
                    .any(|x| x.as_ref().eq_ignore_ascii_case(extension));
            }
    
            false
        }
    }
    

    This new version gives us a few extra features. We can check for multiple extension types and it gives us more flexibility in what types it uses.

    // original
    if is_filetype(&dir_entry, "txt") {}
    
    // Alternate
    if dir_entry.path().has_extension(&["txt"]) {}
    
    // We can also check for multiple extensions
    if dir_entry.path().has_extension(&["png", "jpg", "jpeg", "gif", "bmp"]) {}
    

    That being said, there are some pros/cons:

    • Files with extensions like .tar.gz will be read as having an extension of gz.
    • Files with names starting with a period will not be misread as extensions (Ex: The folder ~/.ssh is correctly identified as having no extension).
    • "magic bytes" at the start of a file are not checked for file types where they are required.