summaryrefslogtreecommitdiff
path: root/js/files.ts
blob: 9ea3787356973f95d972560c9c114587c31c2fab (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Reader, Writer, Seeker, Closer, ReadResult, SeekMode } from "./io";
import * as dispatch from "./dispatch";
import * as msg from "gen/msg_generated";
import { assert } from "./util";
import * as flatbuffers from "./flatbuffers";

/** Open a file and return an instance of the `File` object.
 *
 *       (async () => {
 *         const file = await Deno.open("/foo/bar.txt");
 *       })();
 */
export async function open(
  filename: string,
  mode: OpenMode = "r"
): Promise<File> {
  const builder = flatbuffers.createBuilder();
  const filename_ = builder.createString(filename);
  const mode_ = builder.createString(mode);
  msg.Open.startOpen(builder);
  msg.Open.addFilename(builder, filename_);
  msg.Open.addMode(builder, mode_);
  const inner = msg.Open.endOpen(builder);
  const baseRes = await dispatch.sendAsync(builder, msg.Any.Open, inner);
  assert(baseRes != null);
  assert(msg.Any.OpenRes === baseRes!.innerType());
  const res = new msg.OpenRes();
  assert(baseRes!.inner(res) != null);
  const rid = res.rid();
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  return new File(rid);
}

/** Read from a file ID into an array buffer.
 *
 * Resolves with the `ReadResult` for the operation.
 */
export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
  const builder = flatbuffers.createBuilder();
  msg.Read.startRead(builder);
  msg.Read.addRid(builder, rid);
  const inner = msg.Read.endRead(builder);
  const baseRes = await dispatch.sendAsync(builder, msg.Any.Read, inner, p);
  assert(baseRes != null);
  assert(msg.Any.ReadRes === baseRes!.innerType());
  const res = new msg.ReadRes();
  assert(baseRes!.inner(res) != null);
  return { nread: res.nread(), eof: res.eof() };
}

/** Write to the file ID the contents of the array buffer.
 *
 * Resolves with the number of bytes written.
 */
export async function write(rid: number, p: Uint8Array): Promise<number> {
  const builder = flatbuffers.createBuilder();
  msg.Write.startWrite(builder);
  msg.Write.addRid(builder, rid);
  const inner = msg.Write.endWrite(builder);
  const baseRes = await dispatch.sendAsync(builder, msg.Any.Write, inner, p);
  assert(baseRes != null);
  assert(msg.Any.WriteRes === baseRes!.innerType());
  const res = new msg.WriteRes();
  assert(baseRes!.inner(res) != null);
  return res.nbyte();
}

/** Seek a file ID to the given offset under mode given by `whence`.
 *
 */
export async function seek(
  rid: number,
  offset: number,
  whence: SeekMode
): Promise<void> {
  const builder = flatbuffers.createBuilder();
  msg.Seek.startSeek(builder);
  msg.Seek.addRid(builder, rid);
  msg.Seek.addOffset(builder, offset);
  msg.Seek.addWhence(builder, whence);
  const inner = msg.Seek.endSeek(builder);
  await dispatch.sendAsync(builder, msg.Any.Seek, inner);
}

/** Close the file ID. */
export function close(rid: number): void {
  const builder = flatbuffers.createBuilder();
  msg.Close.startClose(builder);
  msg.Close.addRid(builder, rid);
  const inner = msg.Close.endClose(builder);
  dispatch.sendSync(builder, msg.Any.Close, inner);
}

/** The Deno abstraction for reading and writing files. */
export class File implements Reader, Writer, Seeker, Closer {
  constructor(readonly rid: number) {}

  write(p: Uint8Array): Promise<number> {
    return write(this.rid, p);
  }

  read(p: Uint8Array): Promise<ReadResult> {
    return read(this.rid, p);
  }

  seek(offset: number, whence: SeekMode): Promise<void> {
    return seek(this.rid, offset, whence);
  }

  close(): void {
    close(this.rid);
  }
}

/** An instance of `File` for stdin. */
export const stdin = new File(0);
/** An instance of `File` for stdout. */
export const stdout = new File(1);
/** An instance of `File` for stderr. */
export const stderr = new File(2);

export type OpenMode =
  /** Read-only. Default. Starts at beginning of file. */
  | "r"
  /** Read-write. Start at beginning of file. */
  | "r+"
  /** Write-only. Opens and truncates existing file or creates new one for
   * writing only.
   */
  | "w"
  /** Read-write. Opens and truncates existing file or creates new one for
   * writing and reading.
   */
  | "w+"
  /** Write-only. Opens existing file or creates new one. Each write appends
   * content to the end of file.
   */
  | "a"
  /** Read-write. Behaves like "a" and allows to read from file. */
  | "a+"
  /** Write-only. Exclusive create - creates new file only if one doesn't exist
   * already.
   */
  | "x"
  /** Read-write. Behaves like `x` and allows to read from file. */
  | "x+";

/** A factory function for creating instances of `File` associated with the
 * supplied file name.
 * @internal
 */
export function create(filename: string): Promise<File> {
  return open(filename, "w+");
}