rustcliphotographyautomation

Building My First Rust CLI to Automate Photography Workflows

Building My First Rust CLI to Automate Photography Workflows
Alexy Van Den Abele|30.01.26|4 min read

As a photographer, the most tedious part of the job isn't the editing or the shooting—it's the data management. Coming home after a long shoot (or multiple shoots in a row) and staring at an SD card full of unstructured files is daunting.

I wanted a way to simply plug in my SD card, run a command, and have everything organized neatly into folders by date or "session," without me having to manually drag and drop hundreds of files.

This need for automation became the perfect excuse to dive into Rust and build my first real Command Line Interface (CLI) tool: image-processor.

The Problem

My typical workflow involves multiple shooting sessions. Sometimes they are days apart, but often they are just hours apart (e.g., a morning shoot and an evening event).

When you dump an SD card, you usually get a flat list of files like:

  • _MG_1001.CR2 (Taken at 10:00 AM)
  • _MG_1002.CR2 (Taken at 10:05 AM)
  • ...
  • _MG_1200.CR2 (Taken at 08:00 PM)

Manually separating these into 2024-01-30_Morning_Shoot and 2024-01-30_Evening_Event is a manual process prone to mistakes. I wanted a tool that could "look" at the time gaps between photos and decide: "Hey, these photos were taken 6 hours apart, they probably belong to different sessions."

Why Rust?

I could have written this in Python or Bash in an hour. But I chose Rust for a few specific reasons:

  1. Learning Experience: I had been reading about Rust's safety and performance and wanted a practical project to apply what I'd learned.
  2. Reliability: When dealing with my raw photo files (my "negatives"), I don't want a script that fails silently or corrupts data. Rust's strict type system and error handling (Result<T, E>) forced me to handle every edge case—like what happens if the disk is full or a file is unreadable.
  3. Distribution: Compiling a single binary that I can just drop into /usr/local/bin and run anywhere is incredibly convenient. No virtual environments, no missing dependencies.
  4. CLI Ecosystem: Rust has an amazing ecosystem for CLIs. The clap crate makes parsing arguments a breeze, and indicatif provides beautiful progress bars with minimal effort.

How It Works

The logic is relatively simple but robust. Here is the high-level flow of the application:

  1. Scan & Sort: The tool recursively scans the input directory for .CR2 (Canon Raw) and .MP4 files. Crucially, it sorts them by their sequence number extracted from the filename (e.g., _MG_1001.CR2 -> 1001). This ensures that even if file modification times are wonky, the order of shots is preserved.
  2. Read Metadata: It reads the EXIF data for images and filesystem metadata for videos to determine the creation timestamp.
  3. Group into Sessions: This is the core logic. It iterates through the sorted files and checks the time difference between the current file and the previous one.
    • If the gap is larger than the threshold (default: 6 hours), a new "Session" is started.
    • Folders are named by date (e.g., 2024-01-30). If multiple sessions happen on the same day, it appends a suffix (e.g., 2024-01-30_a, 2024-01-30_b).
  4. Copy with Resume Support: It copies the files to the destination.

The "Resume" Feature

One specific technical choice I'm proud of is the state management. Copying 64GB of data takes time. If the transfer crashes or I accidentally pull the cable, I didn't want to start over.

I implemented a simple state tracker using serde_json. After every successful file copy, the tool updates a .image-processor-state.json file. When you run the command again, it checks this file and skips everything that has already been successfully transferred.

// A simplified view of how state is handled
#[derive(Serialize, Deserialize)]
struct SessionState {
    last_processed_file: String,
    current_session_id: usize,
    // ...
}

Conclusion

Building image-processor taught me that Rust is not just for systems programming; it's a fantastic language for everyday tools. The initial development time was slightly higher than Python, but the result is a tool I trust completely with my professional work.

You can find the full source code and documentation on GitHub: Alexy-vda/image-processor

Now, instead of spending 20 minutes organizing folders, I just run:

image-processor -i /Volumes/EOS_DIGITAL -o ~/Photos

...and go make a coffee while Rust does the heavy lifting.