-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9ed95cd
commit ee007c4
Showing
5 changed files
with
204 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters