Path starts_with does not detect all files with same base

Hi !

I want to check if a file name starts with a specific string.
In that way, I'm using 'starts_with' on path.
I saw that this need to be the same base path, but when I tried to check for the filename it fails.

I tried this code:

// Dir '/mydir' contains :
// - image.jpg
// - doc.pdf
// - video.mp4
// - video.mp4-720p.vp8
// - video.mp4-1080p.vp8
// - video.mp4-720p.vp9
// - video.mp4-1080p.vp9
// - other-video.mp4

// So dirpath = "/mydir" and starting_with = "video.mp4"
fn remove_file(&self, dirpath: &PathBuf, starting_with: &str){
    // if !dirpath.is_dir() {
    //     return err;
    // }

    let readdir = match read_dir(&dirpath) {
        Ok(r) => r,
        Err(e) => {
            // return err;
        }
    };

    let starts_with = dirpath.join(starting_with);

    let files: Vec<PathBuf> = readdir
        .filter_map(|r| r.ok())
        .map(|f| f.path())
        .filter(|f| f.is_file() && f.starts_with(starts_with))
        .collect();

    debug!(
        "{} files found starting with '{}' in '{}'",
        files.len(),
        starting_with,
        dirpath.display()
    );

    // some code on 'files'
}

I expected to see this happen:
the log must print '5' (the 4 transcoded video + the original one)

Instead, this happened:
1 file found (only the original)

rustc --version --verbose:

rustc 1.70.0 (90c541806 2023-05-31)
binary: rustc
commit-hash: 90c541806f23a127002de5b4038be731ba1458ca
commit-date: 2023-05-31
host: x86_64-unknown-linux-gnu
release: 1.70.0
LLVM version: 16.0.2

So do I misundertand something ? Or is it a problem in the API ?
(I think that's me at 95% but don't know where is my mistake)

I am wondering if I have to get OsStr and next &str to check it, but it maybe will not be really clean to that (a lot of options to handle)

Thank you for your help !

The former is overwhelmingly more likely.

Paths aren't compared as raw strings! They have structure, they have components. The file name "foobar" doesn't start with the filename "foo".

What you probably want is called a "glob".

This is deliberate. starts_with considers paths segment-by-segment (a whole directory or file name), not character-by-character. The documentation includes an example demonstrating this that's very similar to your code.

You can use path.file_name() to extract the name (the last path segment) as an Option<&OsStr>, and work with that to do string comparisons on the name like this, or convert it to an Option<&str>. Note that on some OSes, not every filename is representable as a str, but in practice most filenames you're likely to encounter in the wild can be.

4 Likes

Thanks to you two

Effectively I misunderstood the structure of Path in rust.

So the code is now :

let files: Vec<PathBuf> = readdir
            .filter_map(|r| r.ok())
            .map(|f| f.path())
            .filter(|f| {
                f.is_file()
                    && f.file_name()
                        .and_then(OsStr::to_str)
                        .is_some_and(|n| n.starts_with(starting_with))
            })
            .collect();

Thank you ! :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.