Rust std::fs: Parsing Directories & File Entries with DirEntry

Introduction

The Rust programming language provides a robust and efficient way to interact with the file system, making it a popular choice for systems programming and file manipulation tasks. In this mini how-to guide, we will explore the powerful capabilities of the DirEntry struct, which is part of the Rust standard library's std::fs module. DirEntry enables developers to navigate and extract valuable information from directory entries effortlessly, making it an essential tool for file-related operations.

Understanding DirEntry in Rust

Let’s first understand what a rust DirEntry object is before we approach using this struct in a practical problem.

What is DirEntry

DirEntry is a struct that plays a pivotal role in Rust's file system handling. It is returned by the ReadDir iterator, allowing you to access and manipulate properties of individual directory entries with ease. With DirEntry, you can effortlessly gather a wealth of information about files and directories, including their path, metadata, file type, and more.

Extracting DirEntry Information

When working with DirEntry, you can extract an array of essential attributes from directory entries. Some of the key information you can obtain include:

  • Path: Discover the full path to the directory entry.

  • Metadata: Access detailed file metadata, including permissions, whether it's a directory or a file, creation, modification, and access times, file type, and Unix mode.

  • OS String: Retrieve an OS-specific string representation of the directory entry.

  • Filename: Obtain the name of the directory entry.

  • Filetype: Determine the type of file or directory entry using various methods, including the Metadata struct.

Problem Definition

Before diving into the details of how to interact with DirEntry in Rust, it's crucial to define a practical problem that we'll address throughout this guide. Let's set the stage by defining our problem:

Problem: Imagine you are building a file management tool in Rust. You need to create a feature that lists all the files and directories within a specified directory, displaying their names, sizes, and last modification dates. Additionally, you want to identify and mark directories with a special symbol in the list. How can you achieve this efficiently using Rust's DirEntry?

In the following sections, we will break down this problem step by step, demonstrating how to use DirEntry to solve it effectively. By the end of this mini how-to, you'll have a clear understanding of how to harness the power of Rust's DirEntry to manage and extract valuable information from directory entries in your projects. Let's get started!

Solution

Let’s solve our practical problem with the following steps:

Import Required Libraries

To begin we need to import the std::fs module using the following declaration.

use std::fs;

Optionally we can import DirEntryExt2 inside our main.rs file which imports Unix-specific extension methods. This is an experimental implementation available for nightly Rust.


#![feature(dir_entry_ext2)]
use std::os::unix::fs::DirEntryExt2;

Creating a Rust Module: dir_entry_mod

In this example I will leverage Rust’s powerful module system to create a mod that handles the collection of DirEntry objects. This will allow me to write clean and modular code, allowing me to keep my main.rs file reserved for business logic.

Inside of the main projects src directory I have a mods/ directory which contains a series of mods.

An example of a mod directory structure inside of a rust project

Example mod directory structure

Inside of the mods/ directory create the file mod.rs, the full path should be src/mods/mod.rs. In this mod file we will add a pub mod entry for our dir_entry_mod. Using the pub keyword allows this module to be accessible to external modules.

pub mod dir_entry_mod;

If further nesting occurs, such as each mod having it’s own directory we need to also create a mod.rs in that directory as well such as src/mods/dire_entry_mod/mod.rs in order to make the nested module accessible.

I will define a pub mod of direntries since I will write my function to collect DirEntry objects inside of dir_entries.rs.

pub mod dir_entries;

Once we have linked the mods and made them accessible externally using the pub keyword we can call the main mods directory inside our main.rs file under our imports.

mod mods;

Defining Our Mod Function: get_dir_entries()

Once we have our mod structure in place we can proceed to write the get_dir_entries() function that collects DirEntry. We can define this function inside of src/mods/dir_entry_mod/dir_entries.rs which will take a string reference to a directory we want to collect from. This directory will be read using the read_dir() method. Finally our function will return a vector of DirEntry structs. Each DirEntry will correspond to a unique entry encountered inside the directory we passed as an argument to our function.

pub fn get_dir_entries(read_dir_path: &str) -> Result<Vec<fs::DirEntry>, std::io::Error> {
// NEXT STEPS GO HERE
}

Creating the Vector to Store Our DirEntry Structs

Inside of our rust function we need to define a variable that will hold our DirEntry values. We will define this variable with the help of the vec! macro.

let mut dir_entries = vec![];

Next we need to call the read_dir() method to read the path we passed to our function as an argument. For each DirEntry we need to push that object to dir_entries vector.

 for dir_entry in fs::read_dir(read_dir_path)? {
        let dir_entry = dir_entry?;
        dir_entries.push(dir_entry);
}

Once we have collected all DirEntry objects we can return our vec.

Ok(dir_entries)

Interacting with DirEntry Objects

Once we have the function created to gather our DirEntry objects we can access various DirEntry methods in order to find out information about this specific DirEntry such as path, metadata, OS string, filename, and file type.

Inside of our main function inside of main.rs we can set a variable that contains a reference to a path we want to collect DirEntry objects from.

let dir_path = "./tmp/";

Next we can pass this variable to our function and store the result inside of a variable.

let dir_entries = mods::dir_entry_mod::dir_entries::get_dir_entries(dir_path);

Next we can use rust’s pattern matching capabilities with the match keyword which gives the engineer access to flow control.

Ok(dir_entries) => {
// FLOW CONTROL LOGIC GOES HERE
}

Inside our flow control statement we can iterate over our DirEntry object vector using a for loop.

 for dir_entry in dir_entries {
// LOOP OPERATIONS GO HERE
}

Inside of our loop can iterate over each DirEntry and access information such as ile name, path, metadata, and file type.

Getting the DirEntry File Name

We can use the file_name() method on the DirEntry object to access the DirEntry object filename.

// FILENAME
let dir_entry_file_name = dir_entry.file_name();

Getting the DirEntry Path

We can use the path() method on the DirEntry object to gain access to the DirEntry object path.

// PATH
let dir_entry_path = dir_entry.path();

Getting the DirEntry Metadata

We can use the metadata() method on the DirEntry object to gain access to the DirEntry object metadata methods.

// GET METADATA
let dir_entry_metadata = dir_entry.metadata().unwrap();

Once we have stored the DirEntry’s Metadata object we can access the following:

Getting the DirEntry Permissions

We can access the DirEntry permissions with the permissions() method.

// GET  PERMISSIONS
let dir_entry_permissions = dir_entry_metadata.permissions();

Check if DirEntry is a Directory

We can check to see if the DirEntry is a directory with the is_dir() method.

// GET IS_DIR BOOLEAN
let dir_entry_is_dir_bool = dir_entry_metadata.is_dir();

Check if DirEntry is a Symbolic Link (Symlink)

We can check to see if the DirEntry is a symlink with the is_symlink() method.

// GET IS_SYMLINK BOOL
let dir_entry_is_symlink_bool = dir_entry_metadata.is_symlink();

Check if DirEntry is a File

We can check to see if the DirEntry is a file with the is_file() method.

// GET IS_FILE BOOL
let dir_entry_is_file_bool = dir_entry_metadata.is_file();

Getting the DirEntry Filetype from Metadata

We can get the DirEntry filetype directly from our Metadata object using the file_type() method.

// GET METADATA FILETYPE
let dir_entry_meta_filetype = dir_entry_metadata.file_type();

Getting the DirEntry Mode (IF Unix)

If we are working with Unix DirEntry objects we can access the unix mode by firsting importing MetadataExt. After which we can call the mode() method.

// GET Unix MODE
let dir_entry_mode = dir_entry_metadata.mode();

Getting the DirEntry Byte Size

We can get the size of the DirEntry in bytes using the len() method.

// GET Byte LEN of file
let dir_entry_byte__len = dir_entry_metadata.len();

Getting the DirEntry Created Time

We can get the DirEntry created time using the created() method.

// GET CREATED TIME
let dir_entry_created = dir_entry_metadata.created();

Getting the DirEntry Modified Time

We can get the DirEntry modified time using the modified() method.

// GET MODIFIED TIME
let dir_entry_modified = dir_entry_metadata.modified();

Getting the DirEntry Accessed Time

We can get the DirEntry accessed time using the accessed() method.

// GET ACCESSED TIME
let dir_entry_accessed = dir_entry_metadata.accessed();

Getting the DireEntry File Type

Another method to determine the filetype of the DirEntry object is to use the file_type() method directly on the DirEntry itself.

```
// GET FILETYPE
let dir_entry_filetype = dir_entry.file_type().unwrap();
```

Putting Our Solution Together

Below is a complete code example from our code solution, you can also find this project on GitHub.

Conclusion

In conclusion, this mini how-to has provided a concise yet comprehensive guide on how to effectively interact with directory entries in Rust using the DirEntry struct from the standard library. By demonstrating how to access and utilize various attributes and methods associated with DirEntry, such as path, metadata, and file type, we have equipped you with the knowledge and tools to navigate and manipulate directory contents in your Rust applications.

By presenting a clear problem-solving approach, this guide has empowered you to tackle real-world scenarios involving directory manipulation with confidence. As you continue to explore the Rust programming language and its libraries, this newfound understanding of DirEntry and directory interactions will undoubtedly prove invaluable in your coding endeavors.

If you have any questions feel free to email me or reach out to me on social media. Until next time!

Previous
Previous

How to Install Google Chrome on Debian Linux

Next
Next

Rust std::fs: Creating Directories with DirBuilder