Skip to content

Commit

Permalink
add filesystem
Browse files Browse the repository at this point in the history
  • Loading branch information
EmeraldBlock committed Dec 6, 2023
1 parent 9ed95cd commit ee007c4
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 1 deletion.
15 changes: 14 additions & 1 deletion js/cpu/computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@ import type Monitor from "../monitor.js";
import type { Program } from "./program.js";
import help from "./programs/help.js";
import color from "./programs/color.js";
import { mkdir, touch, cat, writeFile, ls } from "./programs/fs.js";
import { Filesystem } from "./filesystem.js";

export class Computer {
public monitor: Monitor;
public programs: Map<string, Program>;
public filesystem: Filesystem;

constructor(monitor: Monitor) {
this.monitor = monitor;
this.programs = new Map(Object.entries({
help,
color,
mkdir,
touch,
cat,
"write-file": writeFile,
ls,
}));
this.filesystem = new Filesystem();
}

async init() {
await this.filesystem.init();
}

async run(str: string) {
Expand All @@ -21,7 +34,7 @@ export class Computer {

const cmd = res[0];
if (this.programs.has(cmd)) {
this.programs.get(cmd)!({ out: (...args) => this.monitor.print(...args), monitor: this.monitor }, res);
await this.programs.get(cmd)!({ out: (...args) => this.monitor.print(...args), monitor: this.monitor }, res);
return;
}

Expand Down
9 changes: 9 additions & 0 deletions js/cpu/file.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type Dir = {
type: "dir",
entrys: Record<string, number>,
}
export type File = {
type: "file",
contents: string,
}
export type Entry = Dir | File;
114 changes: 114 additions & 0 deletions js/cpu/filesystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Dir, Entry } from "./file";

const promisify = <T>(req: IDBRequest<T>) => new Promise<T>((res, rej) => {
req.addEventListener("success", function() {
res(this.result);
});
req.addEventListener("error", function() {
alert(this.error);
rej(this.error);
// TODO: error handling
});
});

const parse = (path: string) => {
return path.trim().split("/").filter((name) => name !== "");
};

export class Filesystem {
public root!: IDBValidKey;
public db!: IDBDatabase;

async init() {
const request = window.indexedDB.open("filesystem", 1);
request.addEventListener("upgradeneeded", async function() {
const db = this.result;
const info = db.createObjectStore("info");
const files = db.createObjectStore("files", { autoIncrement: true })

const req = files.add({
type: "dir",
entrys: {},
} satisfies Dir);
info.put(await promisify(req), "root");
});
this.db = await promisify(request);
this.root = await promisify(this.db.transaction("info").objectStore("info").get("root"));
}

async walk(base: IDBValidKey, names: string[]) {
for (const name of names) {
const req = this.db.transaction("files").objectStore("files").get(base) as IDBRequest<Entry>;
const entry = await promisify(req);
if (entry.type !== "dir") throw "not a directory";
if (!(name in entry.entrys)) throw "doesn't exist";
base = entry.entrys[name];
}
return base;
}

async makeDir(path: string) {
const names = parse(path);
if (names.length === 0) throw "no name specified";
const target = names.pop()!;
const dir = await this.walk(this.root, names);

const entry = await promisify(this.db.transaction("files").objectStore("files").get(dir) as IDBRequest<Entry>);
if (entry.type !== "dir") throw "not a directory";
if (target in entry.entrys) throw "item already exists";

const request = this.db.transaction("files", "readwrite").objectStore("files").add({
type: "dir",
entrys: {},
});
entry.entrys[target] = await promisify(request) as number;
await promisify(this.db.transaction("files", "readwrite").objectStore("files").put(entry, dir));
}

async makeFile(path: string) {
const names = parse(path);
if (names.length === 0) throw "no name specified";
const target = names.pop()!;
const dir = await this.walk(this.root, names);

const entry = await promisify(this.db.transaction("files").objectStore("files").get(dir) as IDBRequest<Entry>);
if (entry.type !== "dir") throw "not a directory";
if (target in entry.entrys) throw "item already exists";

const request = this.db.transaction("files", "readwrite").objectStore("files").add({
type: "file",
contents: "",
});
entry.entrys[target] = await promisify(request) as number;
await promisify(this.db.transaction("files", "readwrite").objectStore("files").put(entry, dir));
}

async readFile(path: string) {
const names = parse(path);
const target = await this.walk(this.root, names);

const entry = await promisify(this.db.transaction("files").objectStore("files").get(target) as IDBRequest<Entry>);
if (entry.type !== "file") throw "not a file";
return entry.contents;
}

async writeFile(path: string, contents: string) {
const names = parse(path);
if (names.length === 0) throw "no name specified";
const target = await this.walk(this.root, names);

await promisify(this.db.transaction("files", "readwrite").objectStore("files").put({
type: "file",
contents,
}, target));
}

async readDir(path: string) {
const names = parse(path);
const target = await this.walk(this.root, names);

const entry = await promisify(this.db.transaction("files").objectStore("files").get(target) as IDBRequest<Entry>);
if (entry.type !== "dir") throw "not a directory";
return entry.entrys;
}
}
66 changes: 66 additions & 0 deletions js/cpu/programs/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { Program } from "../program.js";

export const mkdir: Program = async (sys, argv) => {
if (argv.length < 2) {
sys.out("need argument\n");
return;
}
try {
await sys.monitor.computer.filesystem.makeDir(argv[1]);
} catch (err) {
sys.out(`${err}\n`);
return;
}
};

export const touch: Program = async (sys, argv) => {
if (argv.length < 2) {
sys.out("need argument\n");
return;
}
try {
await sys.monitor.computer.filesystem.makeFile(argv[1]);
} catch (err) {
sys.out(`${err}\n`);
return;
}
};

export const cat: Program = async (sys, argv) => {
if (argv.length < 2) {
sys.out("need argument\n");
return;
}
try {
sys.out(await sys.monitor.computer.filesystem.readFile(argv[1]));
} catch (err) {
sys.out(`${err}\n`);
return;
}
};

export const writeFile: Program = async (sys, argv) => {
if (argv.length < 3) {
sys.out("need 2 arguments\n");
return;
}
try {
await sys.monitor.computer.filesystem.writeFile(argv[1], argv[2] + "\n");
} catch (err) {
sys.out(`${err}\n`);
return;
}
};

const listing = (entrys: Record<string, number>) => {
return Object.keys(entrys).map(s => s + "\n").join("");
};

export const ls: Program = async (sys, argv) => {
try {
sys.out(listing(await sys.monitor.computer.filesystem.readDir(argv[1] ?? "/")));
} catch (err) {
sys.out(`${err}\n`);
return;
}
};
1 change: 1 addition & 0 deletions js/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default class Monitor {
}

async boot() {
await this.computer.init();
await sleep(800);
const size = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
this.print(size < 76 ? text1s : text1);
Expand Down

0 comments on commit ee007c4

Please sign in to comment.