跳转至

简单的文件迭代器

简单的文件迭代器

#pragma once


/**
 * linux/macos 下使用
 * 一个简单的文件迭代器
 */


#include <string>
#include <queue>


#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

// fs
namespace fs {
//
struct FilesIter {

private:
  struct DirState {
    std::string path;
    DIR *dir = nullptr;
    long offset = 0;
    DirState() {}
    ~DirState() {}
    DirState(std::string p, DIR *d, long off)
        : path(std::move(p)), dir(d), offset(off) {}
    DirState(const DirState &rhs) = default;
    DirState &operator=(const DirState &) = default;
  };

  std::queue<DirState> que;

public:
  explicit FilesIter(const std::string &root) {
    que.emplace(DirState(root, nullptr, 0));
  }
  ~FilesIter() {}

  FilesIter(const FilesIter &) = default;
  FilesIter &operator=(const FilesIter &) = default;

  std::optional<std::string> next() { return fetch_next(); }

private:
  std::optional<std::string> fetch_next() {
    while (!que.empty()) {
      auto &cur = que.front();
      if (cur.dir == nullptr) {
        cur.dir = opendir(cur.path.c_str());
        if (cur.dir == nullptr) {
          que.pop();
          continue;
        }
        seekdir(cur.dir, 0);
      }

      dirent *entry = readdir(cur.dir);
      if (entry == nullptr) {
        closedir(cur.dir);
        que.pop();
        continue;
      }

      std::string filename = entry->d_name;
      if (filename == "." || filename == "..") {
        continue;
      }

      std::string fullpath = cur.path + "/" + filename;

      struct stat entry_stat;
      if (stat(fullpath.c_str(), &entry_stat) == 0) {
        if (S_ISDIR(entry_stat.st_mode)) {
          que.emplace(fullpath, nullptr, 0);
        } else if (S_ISREG(entry_stat.st_mode)) {
          cur.offset = telldir(cur.dir);
          return fullpath;
        }
      }
    }
    return {};
  }
};
}; // namespace fs