Ch11.18: Filesystem APIs

Overview

::fast_io::dir_file opens a directory handle for enumeration. Combined with at(), current(), and recursive(), it provides a powerful, type-safe way to traverse the filesystem. Each iteration yields a directory_entry with functions to query the filename, extension, and file type.

Opening a Directory

Construct a dir_file from a path. The constructor accepts anything convertible to a C string — C strings, string_view, u8string, or wrapped with os_c_str.


#include <fast_io.h>
#include <fast_io_device.h>

int main(int argc, char** argv) {
    using namespace ::fast_io::iomnp;

    // From a string literal (no wrapping needed)
    ::fast_io::dir_file df(u8"/home/user/docs");

    // From a runtime C string (must wrap with os_c_str)
    ::fast_io::dir_file df2(::fast_io::mnp::os_c_str(argv[1]));
}

The at() Function

Call at(df) to get an at_entry — a lightweight handle anchored at the directory. This handle is used by current() and recursive() to enumerate entries, and can also be used to open files relative to the directory (as shown in Ch11.1).


::fast_io::dir_file df(u8"my_directory");
auto entry = at(df);  // at_entry anchored at my_directory

Iterating Immediate Children: current()

current(at(df)) returns a range that yields each immediate child of the directory (non-recursive). Use it in a range-for loop:


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    using namespace ::fast_io::iomnp;

    ::fast_io::dir_file df(u8".");

    for (auto const& ent : current(at(df)))
    {
        if (is_dot(ent)) continue;  // skip . and ..

        switch (type(ent)) {
            using enum ::fast_io::file_type;
        case directory: print("[DIR]  "); break;
        case symlink:   print("[LINK] "); break;
        case regular:   print("[FILE] "); break;
        default:        print("[OTHER]");
        }
        println(::fast_io::mnp::code_cvt(u8filename(ent)));
    }
}

Recursive Traversal: recursive()

recursive(at(df)) walks all descendants of the directory, yielding every file and subdirectory it contains. This is ideal for searching or processing entire directory trees.


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    using namespace ::fast_io::iomnp;

    ::fast_io::dir_file df(u8"src");

    ::std::size_t cpp_count{};
    ::std::size_t total_lines{};

    for (auto const& ent : recursive(at(df)))
    {
        if (type(ent) != ::fast_io::file_type::regular) continue;

        ::std::u8string_view ext{u8extension(ent)};
        if (ext == u8".cpp" || ext == u8".h")
        {
            ++cpp_count;

            // Load the file and count lines
            ::fast_io::native_file_loader loader(drt(ent));
            for (auto ch : loader)
            {
                if (ch == u8'\n') ++total_lines;
            }
        }
    }

    println("Found ", cpp_count, " C++ files with ", total_lines, " total lines");
}

Directory Entry Functions

Each directory_entry yielded by current() or recursive() exposes these functions:

Function Returns Description
u8filename(ent) char8_t string_view Filename in UTF-8.
u8extension(ent) char8_t string_view Extension (from last . onward) in UTF-8.
u8stem(ent) char8_t string_view Filename without extension in UTF-8.
type(ent) file_type File type (see below).
is_dot(ent) bool True if the name is . or ...
drt(ent) fs_dirent A (fd, name) pair — pass to file constructors or loaders to open the entry.
at(ent) at_entry Anchor *at-style operations relative to this entry.

There are also native_filename(), native_extension(), and native_stem() variants that return the filename in the native encoding (which may not be UTF-8 on all platforms).

The file_type Enum

::fast_io::file_type categorizes filesystem entries:

Value Description
regularRegular file.
directoryDirectory.
symlinkSymbolic link.
blockBlock device.
characterCharacter device.
fifoFIFO (named pipe).
socketSocket.
unknownUnknown type.
not_foundFile does not exist.

Opening Files Relative to Directory Entries

Use drt(ent) to get a (fd, name) pair that file constructors and loaders can open directly. This is more efficient than building a full path, and avoids race conditions from path resolution.


#include <fast_io.h>
#include <fast_io_device.h>

int main() {
    using namespace ::fast_io::iomnp;

    ::fast_io::dir_file df(u8"data");

    for (auto const& ent : current(at(df)))
    {
        if (type(ent) != ::fast_io::file_type::regular) continue;

        // Open the entry directly using drt(ent)
        ::fast_io::native_file_loader loader(drt(ent));
        println(::fast_io::mnp::code_cvt(u8filename(ent)), ": ", loader.size(), " bytes");
    }
}

Key Takeaways