-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfs.ts
195 lines (193 loc) · 6.4 KB
/
fs.ts
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import Machine from "./models/machine.ts";
import { ArangoMachine } from "./models/mod.ts";
import { Document } from "https://deno.land/x/darango/mod.ts";
import { posix as path } from "https://deno.land/[email protected]/path/mod.ts";
export interface MockStat {
name: string;
isFile: boolean;
isDirectory: boolean;
parent: string;
fullPath: string;
ext: string;
}
export default class MockFS {
private _fs: { [key: string]: string | null };
constructor(private machine: Document<ArangoMachine>) {
if (typeof machine.filesystem == "string") {
this._fs = JSON.parse(this.machine.filesystem as string);
} else {
this._fs = this.machine.filesystem as unknown as {
[key: string]: string | null;
};
}
}
private async update(): Promise<void> {
this.machine.filesystem = JSON.stringify(this._fs);
await this.machine.update();
}
public async readFile(p: string, cwd: string = "/"): Promise<string> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
const f = this._fs[p];
if (f == null) throw new Error("Cannot read directory as file.");
else if (f === undefined) throw new Error("Path does not exist.");
return f;
}
public async isFile(p: string, cwd: string = "/"): Promise<boolean> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
const f = this._fs[p];
if (f === undefined) throw new Error("Path does not exist.");
return f != null;
}
public async exists(p: string, cwd: string = "/"): Promise<boolean> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
const f = this._fs[p];
return f !== undefined;
}
public async stat(p: string, cwd: string = "/"): Promise<MockStat> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
const f = this._fs[p];
if (f === undefined) throw new Error(`Path ${p} does not exist.`);
const fparse = path.parse(p);
const isFile = f != null;
const isDirectory = !isFile;
return {
name: fparse.name,
isFile,
isDirectory,
ext: fparse.ext,
fullPath: p,
parent: fparse.dir,
};
}
public async mkdir(p: string, cwd: string = "/"): Promise<void> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
if (await this.exists(p)) throw new Error("Path already exists.");
const parse = path.parse(p);
if (!(await this.exists(parse.dir))) {
throw new Error("Parent directory does not exist.");
}
this._fs[p] = null;
await this.update();
}
public async mkdirp(p: string, cwd: string = "/"): Promise<void> {
const recurseDir = (
dir: string,
dirs: string[] = [],
): Promise<string[]> => {
return new Promise((resolve, reject) => {
setTimeout(async () => {
if (await this.exists(dir)) {
const stat = await this.stat(dir);
if (stat.isFile) reject(new Error("Path is not a directory!"));
else resolve(dirs);
} else {
const dirparse = path.parse(dir);
dirs.push(dir);
resolve(await recurseDir(dirparse.dir, dirs));
}
}, 1);
});
};
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
for (const dir of (await recurseDir(p)).reverse()) {
await this.mkdir(dir);
}
}
public async readDir(p: string, cwd: string = "/"): Promise<string[]> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
const res: string[] = [];
const f = this._fs[p];
if (typeof f == "string") throw new Error("Cannot read file as directory.");
else if (f != null) throw new Error(`Path '${p}' does not exist.`);
res.push(
...Object.keys(this._fs).filter((k) => k.startsWith(p) && k != p).map((
k,
) => k.replace(p, "")).filter((k) => k.lastIndexOf("/") <= 0).map((k) =>
k.replace("/", "")
),
);
return res;
}
public async rmdir(p: string, cwd: string = "/"): Promise<void> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
const f = this._fs[p];
if (typeof f == "string") throw new Error("Cannot remove file.");
else if (f != null) throw new Error(`Path '${p}' does not exist.`);
if ((await this.readDir(p)).length > 0) {
throw new Error(`Directory '${p}' is not empty.`);
}
delete this._fs[p];
await this.update();
}
public async rm(
p: string,
recurse: boolean = false,
cwd: string = "/",
): Promise<void> {
await new Promise<void>((resolve, reject) => {
setTimeout(async () => {
try {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
const f = this._fs[p];
if (f == null && !recurse) {
throw new Error("Cannot remove directory.");
} else if (f != null && typeof f != "string") {
throw new Error(`Path '${p}' does not exist.`);
}
if (recurse && f == null) {
const files = await this.readDir(p);
for (const file of files) {
await this.rm(path.resolve(p, file), recurse, cwd);
}
}
delete this._fs[p];
resolve();
} catch (e) {
reject(e);
}
}, 1);
});
await this.update();
}
public async writeFile(
p: string,
data: string,
cwd: string = "/",
): Promise<void> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
await this.ensureFile(p);
const f = this._fs[p];
if (f === null) throw new Error("Cannot write to a directory.");
this._fs[p] = data;
await this.update();
}
public async ensureFile(p: string, cwd: string = "/"): Promise<void> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
if (await this.exists(p)) {
const stat = await this.stat(p);
if (stat.isDirectory) throw new Error("Path is a directory.");
if (stat.isFile) return;
}
const fparse = path.parse(p);
if (!await this.exists(fparse.dir)) {
throw new Error("Parent directory does not exist.");
}
this._fs[p] = "";
await this.update();
}
public async appendFile(
p: string,
data: string,
cwd: string = "/",
): Promise<void> {
if (!path.isAbsolute(p)) p = path.resolve(cwd, p);
await this.ensureFile(p);
const current = await this.readFile(p);
await this.writeFile(p, current + data);
}
public static async getFilesystem(mid: string): Promise<MockFS> {
const machine = await Machine.findMachine(mid);
if (machine == null) throw new Error("Machine not found.");
return new MockFS(machine);
}
}