summaryrefslogtreecommitdiff
path: root/std/node/_fs/_fs_dir.ts
blob: e3830bb317eff405ef2da8faacfc9685f15217e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import Dirent from "./_fs_dirent.ts";

export default class Dir {
  private dirPath: string | Uint8Array;
  private files: Dirent[] = [];
  private filesReadComplete = false;

  constructor(path: string | Uint8Array) {
    this.dirPath = path;
  }

  get path(): string {
    if (this.dirPath instanceof Uint8Array) {
      return new TextDecoder().decode(this.dirPath);
    }
    return this.dirPath;
  }

  /**
   * NOTE: Deno doesn't provide an interface to the filesystem like readdir
   * where each call to readdir returns the next file.  This function simulates this
   * behaviour by fetching all the entries on the first call, putting them on a stack
   * and then popping them off the stack one at a time.
   *
   * TODO: Rework this implementation once https://github.com/denoland/deno/issues/4218
   * is resolved.
   */
  read(callback?: Function): Promise<Dirent | null> {
    return new Promise(async (resolve, reject) => {
      try {
        if (this.initializationOfDirectoryFilesIsRequired()) {
          const denoFiles: Deno.FileInfo[] = await Deno.readdir(this.path);
          this.files = denoFiles.map(file => new Dirent(file));
        }
        const nextFile = this.files.pop();
        if (nextFile) {
          resolve(nextFile);
          this.filesReadComplete = this.files.length === 0;
        } else {
          this.filesReadComplete = true;
          resolve(null);
        }
        if (callback) {
          callback(null, !nextFile ? null : nextFile);
        }
      } catch (err) {
        if (callback) {
          callback(err, null);
        }
        reject(err);
      }
    });
  }

  readSync(): Dirent | null {
    if (this.initializationOfDirectoryFilesIsRequired()) {
      this.files.push(
        ...Deno.readdirSync(this.path).map(file => new Dirent(file))
      );
    }
    const dirent: Dirent | undefined = this.files.pop();
    this.filesReadComplete = this.files.length === 0;

    return !dirent ? null : dirent;
  }

  private initializationOfDirectoryFilesIsRequired(): boolean {
    return this.files.length === 0 && !this.filesReadComplete;
  }

  /**
   * Unlike Node, Deno does not require managing resource ids for reading
   * directories, and therefore does not need to close directories when
   * finished reading.
   */
  close(callback?: Function): Promise<void> {
    return new Promise((resolve, reject) => {
      try {
        if (callback) {
          callback(null);
        }
        resolve();
      } catch (err) {
        if (callback) {
          callback(err);
        }
        reject(err);
      }
    });
  }

  /**
   * Unlike Node, Deno does not require managing resource ids for reading
   * directories, and therefore does not need to close directories when
   * finished reading
   */
  closeSync(): void {
    //No op
  }

  async *[Symbol.asyncIterator](): AsyncIterableIterator<Dirent> {
    try {
      while (true) {
        const dirent: Dirent | null = await this.read();
        if (dirent === null) {
          break;
        }
        yield dirent;
      }
    } finally {
      await this.close();
    }
  }
}