Home > Software engineering >  Rust: How to expand path, join path, and glob files?
Rust: How to expand path, join path, and glob files?

Time:09-21

I'm learning Rust and I'm trying to convert a simple Ruby example which does:

  1. File expand path
  2. File join paths
  3. Dir glob
  4. Prints files found in Dir glob

Here's the Ruby example:

output_path = '~/Documents'
pattern = File.expand_path(File.join(output_path, '*', '**', '*.txt'))

Dir.glob(pattern) do |path|
  puts path
end

Bonus Nim example:

import os
import strformat
import glob

var outputPath = expandTilde("~/Documents")
var pattern = joinPath(outputPath, "*", "**", "*.txt")

for path in walkGlob(pattern):
  echo path

Here's what I have so far for Rust, however, it doesn't build:

use std::io;
use std::fs;
use std::path::PathBuf;
use glob::glob;

fn main() {
    let output_path: io::Result<PathBuf> = fs::canonicalize("~/Documents");
    let pattern: PathBuf = [output_path, "*", "**", "*.txt"].iter().collect();
    for entry in glob(pattern).expect("Failed to read glob pattern") {
        match entry {
            Ok(path) => println!("{:?}", path.display()),
            Err(e) => println!("{:?}", e),
        }
    }
}

I've tried different variations with no success.

I'm not sure fs::canonicalize actually expands tilde paths. I found an older crate which does: shellexpand::tilde. However, I don't know how to deal with the output it gives. It's not a str or String.

I'm also not sure if I need multiple levels of match on the Result and Option return values from some of those. Ultimately glob wants a str and in all my variations, the first output_path value messes that up, or I don't know how to convert that to a str.

What am I missing? What is the idiomatic way to do this? So far the approaches I've tried seem too verbose. Is there a simple way to do this?

UPDATE

I eventually got it to work if I hard-coded the expanded path:

use std::path::PathBuf;
use glob::glob;

fn main() {
    let pattern_path_buf: PathBuf = ["", "/Users", "ejstembler", "Documents", "*", "**", "*.txt"].iter().collect();
    match pattern_path_buf.to_str() {
        Some(pattern) => {
            println!("{:?}", pattern);
            for entry in glob(pattern).expect("Failed to read glob pattern") {
                match entry {
                    Ok(path) => println!("{:?}", path.display()),
                    Err(e) => println!("{:?}", e),
                }
            }
        },
        None => println!("no pattern_path_buf")
    }
}

One weird nuance, is that I had to add a blank string in the beginning of the PathBuf "" to get it to include a leading forward slash, otherwise it would be missing. I found that there's std::path::MAIN_SEPARATOR, however, I have no idea how to convert a char to a &str.

In any case, @Joe_Jingyu provided the accepted answer with shellexpand::tilde working. Thanks Joe!

CodePudding user response:

Here is my code of using shellexpand::tilde to expand tilde.

use std::path::PathBuf;
use glob::glob;
use shellexpand;

fn main() {
    let expanded_path = shellexpand::tilde("~/Documents/");
    let pattern : PathBuf = [&expanded_path, "*", "**", "*.txt"].iter().collect();

    for entry in glob(pattern.to_str().unwrap()).expect("Failed to read glob pattern") {
        match entry {
            Ok(path) => println!("{:?}", path.display()),
            Err(e) => println!("{:?}", e),
        }
    }
}

Here is a post on how to get &str from a Cow returned from shellexpand::tilde.

  • Related