Practice Rust and TAURI: Make an Image Viewer #3

mar-m. nakamura
4 min readFeb 13, 2022

--

I will make an image viewer and study. 😊
It is a continuation from #2.

Last time, I made a drive selection process.
This time, I will try to get the directory and file entries of the PC from the selected drive.

This time’s goal is like this.

Write a process to get a list of files and directories.

Edit : tauri_imgv/src-tauri/src/main.rs

Only the blocks to be added are listed here. (All codes are posted at the end of this article)

Add the following declaration and structure.

use std::fs::{self, read};struct DirEntries(Mutex<Vec<PathBuf>>);// Number of subdirectories in the current entry list.
struct SubDirectoriesCount(Mutex<usize>);

Add two commands, scan_dir() and count_sub_dir().

#[tauri::command]
fn scan_dir(
dir_entries: State<DirEntries>,
active_path: State<ActivePath>,
sub_dir_count: State<SubDirectoriesCount>,
) -> Vec<PathBuf> {
// Directory scan
let path = &*active_path.0.lock().unwrap();
let dir = fs::read_dir(path).expect("❌DIR NOT FOUND");
let mut entries:Vec<PathBuf> = Vec::new();
let mut files:Vec<PathBuf> = Vec::new();
for entry in dir {
if let Ok(entry) = entry {
if let Ok(metadata) = entry.metadata() {
if metadata.is_dir() {
entries.push(entry.path());
} else {
files.push(entry.path());
}
}
}
}
*sub_dir_count.0.lock().unwrap() = entries.len() + 1;
// Sort files and directories, and Append
entries.sort();
files.sort();
entries.append(&mut files);
// Add parent directory to top
entries.push( match path.parent() {
Some(_parent) => _parent.to_path_buf(),
_ => path.to_path_buf(),
});
entries.rotate_right(1);
// Update state
*dir_entries.0.lock().unwrap() = entries.clone();
entries
}
#[tauri::command]
fn count_sub_dir(sub_dir_count: State<SubDirectoriesCount>) -> usize {
*sub_dir_count.0.lock().unwrap()
}

Add them to main ().
The + mark is the line added this time.

  fn main() {
tauri::Builder::default()
.manage(DriveEntries(scan_drive().into()))
.manage(ActiveDrive(0.into()))
.manage(ActivePath(Default::default()))
+ .manage(DirEntries(Default::default()))
+ .manage(SubDirectoriesCount(Default::default()))
.invoke_handler(tauri::generate_handler![
change_drive,
+ scan_dir,
+ count_sub_dir,
])
:

That’s all for adding to main.rs .

This time, DirEntries and SubDirectriesCountare newly added to the state management.

The command scan_dir returns an array of directory and file entry lists from the selected path. These are sorted by name and the subdirectories are grouped at the top.

The sub_dir_count (State<SubDirectoriesCount>) is provided as a simple means to improve the visibility of the GUI.
But this time I have not used it yet. In the future we will use javascript to change the color of files and directories. (In such cases, it may be more appropriate to design an Object or Multidimensional array, but my skills can’t keep up, so I’ve done this this time.😓)

Next, edit javascript.
The + mark is the line added this time.

Edit : tauri_imgv/dist/main.js

Modify the class RequestToRust {} .

  class RequestToRust {
#invoke;
+ // Number of subdirectories in the current entry list.
+ // (use it later.)
+ #subDirCount;
constructor() {
this.#invoke = window.__TAURI__.invoke;
+ this.#subDirCount = 0;
}
:
:
+ /**
+ * Request directory scan
+ * @returns {Array} Directory entries
+ */
+ async scanDir() {
+ const dirs =
+ await this.#invoke('scan_dir').then(async (_dirs) => {
+ await this.#invoke('count_sub_dir').then((_cnt) => {
+ this.#subDirCount = _cnt;
+ })
+ return _dirs;
+ }).catch(() => []);
+ return dirs;
+ }
+ /**
+ * Returns the number of subdirectories
+ * @returns {number} Subdirectory count
+ */
+ get subDirCount() {
+ return this.#subDirCount;
+ }
}

(May 08, 2022 Fixed: arait async was missing.)

Modify the class UserOperation {} .

4 lines have been added, but the 2 lines added to selectDrive() are for temporary confirmation.

  class UserOperation {
#reqRust;
#drives = [];
#activeDriveNum = 0; // Key of #drives
+ #dirEntries = [];
constructor() {
this.#reqRust = new RequestToRust();
this.#drives = [];
this.#activeDriveNum = 0;
}
async init(payload) {
this.#drives = payload.drives;
document.querySelector('info').innerHTML = this.#drives;
this.#activeDriveNum = await this.#reqRust.changeDrive(this.#activeDriveNum);
+ this.#dirEntries = await this.#reqRust.scanDir();
}
/**
* Select drive
* @param {number} inc increment (or decrement) current drive number
*/
async selectDrive(inc) {
let n = this.#activeDriveNum += parseInt(inc);
let maxNum = this.#drives.length - 1;
n = (n < 0) ? maxNum : n;
n = (n > maxNum) ? 0 : n;
this.#activeDriveNum = await this.#reqRust.changeDrive(n);
console.log("Drv: " + this.#activeDriveNum);
+ // Check the switching of the entry list
+ document.querySelector('list').innerHTML = await this.#reqRust.scanDir();
}
}

That’s all for adding to main.js .

init() of class UserOperation {} issues the Rust commands scan_dir and count_sub_dir to receive directory entries.

As a temporary confirmation, scan_dir is issued and the entry contents are displayed even when the drive is switched.

Let’s run it.

Same image again.

Switch the drive with the left and right keys and check that the entry list is displayed in the green area.

Thank you for reading.
I enjoyed studying this time as well.😊

Next time, I would like to go in and out of subdirectories and display images.

The full source text after this change is as follows. (main.js and main.rs)

--

--

mar-m. nakamura

CatShanty2 Author. I want to develop a modern “3” ! But … My brain is still 8-bit / 32KB.😅