Skip to content

ddn.os.path

The ddn.os.path module provides utilities for file and directory path manipulation. Heavily inspired by Python's pathlib, it offers a cross-platform Path struct for robust path handling on Windows, Linux, and other POSIX systems.

Overview

This module provides the Path struct, which encapsulates filesystem path operations in an object-oriented manner. Instead of working with raw strings and calling separate functions, you can use method chaining and operators to manipulate paths naturally.

The module builds upon and extends standard D path and file utilities, offering additional convenience functions for joining, splitting, normalizing, traversing, and querying filesystem paths.

Key Features

  • Cross-Platform Support: Works on Windows, Linux, and POSIX systems
  • Python pathlib-Inspired API: Familiar interface for Python developers
  • Path Joining: Use the / operator to join path components
  • Path Traversal: Navigate parent directories and ancestors
  • Glob Patterns: Find files matching wildcard patterns
  • Directory Walking: Recursively traverse directory trees
  • File Operations: Read, write, copy, move, and delete files
  • Symlink and Hardlink Support: Create and manage filesystem links
  • File Metadata: Query ownership, permissions, and timestamps

Basic Usage

import ddn.os.path;

void main() {
    // Create Path objects
    auto p1 = Path("foo/bar/baz.txt");
    auto p2 = Path("/tmp/example.txt");

    // Get string representation
    writeln(p1.str());  // "foo/bar/baz.txt"

    // Join paths using / operator
    auto joined = Path("foo") / "bar" / "baz.txt";

    // Get absolute path
    auto abs = p1.absolute();

    // Get parent directory
    auto parent = p1.parent();  // "foo/bar"
}

Path Components

Extract various components from a path:

import ddn.os.path;

auto p = Path("/home/user/documents/report.txt");

// File name (with extension)
p.name();    // "report.txt"

// Stem (file name without extension)
p.stem();    // "report"

// Extension/suffix
p.suffix();  // ".txt"

// Parent directory
p.parent();  // Path("/home/user/documents")

// All parent directories
foreach (par; p.parents()) {
    writeln(par.str());
}
// Output:
// /home/user/documents
// /home/user
// /home

Path Joining

Join path components using the / operator or string concatenation:

import ddn.os.path;

// Using / operator
auto p1 = Path("/home") / "user" / "file.txt";
// Result: /home/user/file.txt

// Building paths incrementally
auto base = Path("/var/log");
auto logFile = base / "app" / "debug.log";

File System Queries

Check file existence and type:

import ddn.os.path;

auto p = Path("/tmp/myfile.txt");

// Check existence
if (p.exists()) {
    // Check type
    if (p.isFile()) {
        writeln("It's a file");
    } else if (p.isDir()) {
        writeln("It's a directory");
    } else if (p.isSymlink()) {
        writeln("It's a symbolic link");
    }
}

// Check if absolute path
if (p.isAbsolute()) { ... }

File Operations

Read and write files:

import ddn.os.path;

auto p = Path("example.txt");

// Write text to file
p.writeText("Hello, World!");

// Read text from file
string content = p.readText();

// Touch (create or update timestamp)
p.touch();

// Remove file
p.remove();

Directory Operations

Create and manage directories:

import ddn.os.path;

auto dir = Path("my/nested/directory");

// Create directory (including parents)
dir.mkdir();  // Creates all intermediate directories

// Remove empty directory
dir.rmdir();

// Remove directory and all contents
dir.remove();  // Recursive removal

Glob Patterns

Find files matching patterns:

import ddn.os.path;

auto dir = Path("/var/log");

// Find all .txt files in directory
auto txtFiles = dir.glob("*.txt");

// Find files in subdirectories
auto nestedTxt = dir.glob("**/*.txt");

// Find hidden files
auto hidden = dir.glob(".*");

// Recursive glob (shorthand for **/ prefix)
auto allLogs = dir.rglob("*.log");

Directory Walking

Traverse directory trees:

import ddn.os.path;

auto root = Path("/project");

// Walk directory tree (top-down by default)
foreach (entry; root.walk()) {
    writeln("Directory: ", entry.root.str());

    // Subdirectories in this directory
    foreach (d; entry.dirs) {
        writeln("  Subdir: ", d.str());
    }

    // Files in this directory
    foreach (f; entry.files) {
        writeln("  File: ", f.str());
    }
}

// Walk bottom-up
foreach (entry; root.walk(false)) { ... }

// Follow symlinks
foreach (entry; root.walk(true, true)) { ... }

Create filesystem links (POSIX):

import ddn.os.path;

auto target = Path("original.txt");
target.writeText("content");

// Create symbolic link
auto symlink = Path("link_to_original.txt");
symlink.symlinkTo(target.str());

// Create hard link
auto hardlink = Path("hardlink_to_original.txt");
hardlink.hardlinkTo(target.str());

// Read symlink target
string linkTarget = symlink.readlink();

Path Resolution and Normalization

import ddn.os.path;

auto p = Path("foo/bar/../baz/./file.txt");

// Resolve to absolute path with normalization
auto resolved = p.resolve();
// Removes . and .. components, makes absolute

// Get absolute path
auto abs = p.absolute();

// Normalize path (remove redundant separators and . / ..)
auto normalized = Path("foo//bar/./baz").normalize();

File Comparison

import ddn.os.path;

auto p1 = Path("/tmp/file1.txt");
auto p2 = Path("/tmp/file2.txt");

// Check if two paths refer to the same file
if (p1.samefile(p2)) {
    writeln("Same file!");
}

File Metadata (POSIX)

import ddn.os.path;

auto p = Path("/etc/passwd");

// Get owner username
string owner = p.owner();

// Get group name
string group = p.group();

// Check special file types
p.isBlockDevice();
p.isCharDevice();
p.isFIFO();
p.isSocket();

Path Conversion

import ddn.os.path;

auto p = Path("/home/user/file.txt");

// Get string representation
string s = p.str();

// Use in string context (via alias this)
writeln(p);  // Automatically converts to string

When to Use

Use this module when you need to:

  • Manipulate filesystem paths in a cross-platform manner
  • Build paths dynamically using intuitive operators
  • Search for files using glob patterns
  • Traverse directory trees recursively
  • Perform file I/O operations with a clean API
  • Work with symlinks and hardlinks
  • Query file metadata and permissions