Comparison Story: EntryPoint

5.  main.cpp — the executable that wires it all together

5.  EntryPoint — main.cpp

EntryPoint is the only translation unit that imports all three library modules. main.cpp is small on purpose — the design goal is to make the wiring itself readable: parse args, configure Output, configure DirNav, register two lambda callbacks, run the walk, print a summary.

5.1  Design Points

  • Module imports replace headers. The first four lines of main.cpp are import cmd_line;, import dir_nav;, import output;, and import std;. CMake's module dependency scanner resolves build order from these imports — no forward declarations, no include-order concerns.
  • Lambda callbacks glue DirNav to Output. Both callbacks capture &out by reference and forward directly to Output::on_dir and Output::on_file. This is the C++ equivalent of the Rust variant's impl DirEvent for TfAppl — a small adapter that adapts one API to the other. Neither library imports the other, so this glue must live in EntryPoint.
  • Help on /h or no arguments. argc == 1 || cl.help() short-circuits before any configuration or traversal — help text prints, main returns 0, no filesystem work is done.
  • Verbose block prints resolved options. When /v is present, all seven options are printed (including their defaults) before the walk starts. The /p line uses std::views::join_with(',') to reassemble the pattern list into a single comma-separated line — a nice C++23 idiom.
  • Traversal failure is a hard error. If dn.visit(...) returns false (path doesn't exist or isn't a directory), main prints to stderr and returns exit code 1. Every other error path — regex compilation, unreadable file, permission-denied subdir — degrades silently and the walk continues.

5.2  Startup Flow

  1. Construct CmdLine cl(argc, argv) — parse argv, apply defaults.
  2. If argc == 1 or /h is present, print help and return.
  3. If /v is present, echo all resolved options.
  4. Construct Output out(cl.hide()) and call out.set_regex(cl.regex()).
  5. Construct DirNav dn(cl.recurse()).
  6. Register two lambda callbacks that forward to out.on_dir and out.on_file.
  7. Add each /p pattern to the walker.
  8. Call dn.visit(cl.path()); on failure, exit 1.
  9. Print the summary line (files visited / matched) and return 0.

5.3  Source — EntryPoint/src/main.cpp

import cmd_line;
import dir_nav;
import output;
import std;

int main(int argc, const char* argv[])
{
    CmdLine cl(argc, argv);

    // Show help when no arguments are supplied or /h is present
    if (argc == 1 || cl.help())
    {
        std::cout << CmdLine::help_text();
        return 0;
    }

    // Verbose: echo all options before searching
    if (cl.verbose())
    {
        std::cout << "Options:\n"
                  << "  /P  " << cl.path()                          << "\n"
                  << "  /r  " << cl.regex()                         << "\n"
                  << "  /s  " << (cl.recurse() ? "true" : "false")  << "\n"
                  << "  /H  " << (cl.hide()    ? "true" : "false")  << "\n";

        auto patterns = cl.patterns();
        if (patterns.empty())
        {
            std::cout << "  /p  (all files)\n";
        }
        else
        {
            std::cout << "  /p  ";
            for (char c : patterns | std::views::join_with(','))
                std::cout << c;
            std::cout << "\n";
        }
        std::cout << "\n";
    }

    // Configure Output
    Output out(cl.hide());
    out.set_regex(cl.regex());

    // Configure DirNav
    DirNav dn(cl.recurse());

    dn.set_dir_handler( [&out](const std::string& d){ out.on_dir(d);  });
    dn.set_file_handler([&out](const std::string& f){ out.on_file(f); });

    for (const auto& pattern : cl.patterns())
        dn.add_pattern(pattern);

    // Run the walk
    if (!dn.visit(std::filesystem::path(cl.path())))
    {
        std::cerr << "error: could not traverse path: " << cl.path() << "\n";
        return 1;
    }

    // Summary
    std::cout << "\n"
              << dn.file_count() << " file(s) visited, "
              << out.match_count() << " file(s) matched\n";

    return 0;
}