Skip to content

Commit 33d031b

Browse files
committed
feat: first pass at gs/os implementation
Signed-off-by: Christian Stewart <christian@aperture.us>
1 parent 7e4463b commit 33d031b

24 files changed

Lines changed: 1583 additions & 278 deletions

gs/os/dir.gs.ts

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,54 @@
11
import * as $ from "@goscript/builtin/index.js";
22
import { ErrUnimplemented } from "./error.gs.js";
3+
import { createFileInfo, getDeno, getNodeFS, newHostError } from "./types_js.gs.js";
4+
import { FileInfoToDirEntry } from "@goscript/io/fs/readdir.js";
35

46
import * as fs from "@goscript/io/fs/index.js"
57

68
type DirEntry = fs.DirEntry;
79

8-
// ReadDir reads the named directory,
9-
// returning all its directory entries sorted by filename.
10-
// If an error occurs reading the directory,
11-
// ReadDir returns the entries it was able to read before the error,
12-
// along with the error.
1310
export function ReadDir(name: string): [$.Slice<DirEntry>, $.GoError] {
14-
// Directory reading not supported in JavaScript environment
11+
const denoObj = getDeno()
12+
if (denoObj?.readDirSync) {
13+
try {
14+
const entries: DirEntry[] = []
15+
for (const entry of denoObj.readDirSync(name)) {
16+
const dirEntry = FileInfoToDirEntry(createFileInfo(entry.name, {
17+
isDirectory: () => entry.isDirectory,
18+
isSymbolicLink: () => entry.isSymlink,
19+
mode: 0,
20+
size: 0,
21+
}))
22+
if (dirEntry !== null) {
23+
entries.push(dirEntry)
24+
}
25+
}
26+
entries.sort((a, b) => (a?.Name() ?? "").localeCompare(b?.Name() ?? ""))
27+
return [$.arrayToSlice(entries), null]
28+
} catch (err) {
29+
return [null, newHostError(err)]
30+
}
31+
}
32+
const nodeFS = getNodeFS()
33+
if (nodeFS?.readdirSync) {
34+
try {
35+
const entries = nodeFS.readdirSync(name, { withFileTypes: true }).map((entry: any) =>
36+
FileInfoToDirEntry(createFileInfo(entry.name, {
37+
isDirectory: () => typeof entry.isDirectory === "function" ? entry.isDirectory() : false,
38+
isSymbolicLink: () => typeof entry.isSymbolicLink === "function" ? entry.isSymbolicLink() : false,
39+
mode: 0,
40+
size: 0,
41+
}))
42+
).filter((entry: DirEntry | null): entry is DirEntry => entry !== null)
43+
entries.sort((a, b) => (a?.Name() ?? "").localeCompare(b?.Name() ?? ""))
44+
return [$.arrayToSlice(entries), null]
45+
} catch (err) {
46+
return [null, newHostError(err)]
47+
}
48+
}
1549
return [null, ErrUnimplemented]
1650
}
1751

18-
// CopyFS copies the file system fsys into the directory dir,
19-
// creating dir if necessary.
20-
//
21-
// Files are created with mode 0o666 plus any execute permissions
22-
// from the source, and directories are created with mode 0o777
23-
// (before umask).
24-
//
25-
// CopyFS will not overwrite existing files. If a file name in fsys
26-
// already exists in the destination, CopyFS will return an error
27-
// such that errors.Is(err, fs.ErrExist) will be true.
28-
//
29-
// Symbolic links in fsys are not supported. A *PathError with Err set
30-
// to ErrInvalid is returned when copying from a symbolic link.
31-
//
32-
// Symbolic links in dir are followed.
33-
//
34-
// New files added to fsys (including if dir is a subdirectory of fsys)
35-
// while CopyFS is running are not guaranteed to be copied.
36-
//
37-
// Copying stops at and returns the first error encountered.
3852
export function CopyFS(dir: string, fsys: fs.FS): $.GoError {
39-
// File system copying not supported in JavaScript environment
4053
return ErrUnimplemented
4154
}
42-

gs/os/exec.gs.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export class ProcAttr {
137137
);
138138
}
139139

140-
// Stub ProcessState for JavaScript environment
140+
// ProcessState mirrors the minimal JavaScript process surface.
141141
export class ProcessState {
142142
public _fields: {}
143143

@@ -159,14 +159,13 @@ export class ProcessState {
159159
);
160160
}
161161

162-
// Signal interface stub
162+
// Signal matches the Go signal interface shape when available.
163163
export type Signal = null | {
164164
Signal(): void
165165
String(): string
166166
}
167167
export const Signal = null as any;
168168

169-
// Stub functions that return ErrUnimplemented
170169
export function Getpid(): number {
171170
return -1 // Not available in JavaScript
172171
}
@@ -195,4 +194,3 @@ export function newHandleProcess(pid: number, handle: number): Process {
195194
export function newPIDProcess(pid: number): Process {
196195
return new Process({Pid: pid})
197196
}
198-

gs/os/exec_posix.gs.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class ProcessState {
3636
return cloned
3737
}
3838

39-
// All methods return stub values for JavaScript environment
39+
// JavaScript does not expose process timing and wait status details.
4040
public UserTime(): any {
4141
return 0 // Duration not available
4242
}
@@ -92,4 +92,3 @@ export class ProcessState {
9292
{ "pid": { kind: $.TypeKind.Basic, name: "number" } }
9393
);
9494
}
95-

gs/os/file_constants_js.gs.ts

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import * as $ from "@goscript/builtin/index.js";
22
import { ErrUnimplemented } from "./error.gs.js";
3+
import { getDeno, getEnv, getNodeFS, getPlatform, newHostError } from "./types_js.gs.js";
34

4-
// JavaScript-specific stubs for file constants and operations
5-
// These provide the required constants and stub implementations
5+
function pickFirstEnv(keys: string[]): string {
6+
for (const key of keys) {
7+
const value = getEnv(key)
8+
if (value !== "") {
9+
return value
10+
}
11+
}
12+
return ""
13+
}
614

715
// File open flags - using values compatible with typical Unix systems
816
export const O_RDONLY = 0
@@ -19,7 +27,7 @@ export const SEEK_SET = 0
1927
export const SEEK_CUR = 1
2028
export const SEEK_END = 2
2129

22-
// LinkError stub for compatibility
30+
// LinkError carries details for link-related path errors.
2331
export class LinkError {
2432
public get Op(): string {
2533
return this._fields.Op.value
@@ -76,23 +84,77 @@ export class LinkError {
7684
}
7785
}
7886

79-
// Directory and file operation stubs
8087
export function Readlink(name: string): [string, $.GoError] {
88+
const denoObj = getDeno()
89+
if (denoObj?.readLinkSync) {
90+
try {
91+
return [denoObj.readLinkSync(name), null]
92+
} catch (err) {
93+
return ["", newHostError(err)]
94+
}
95+
}
96+
const nodeFS = getNodeFS()
97+
if (nodeFS?.readlinkSync) {
98+
try {
99+
return [nodeFS.readlinkSync(name), null]
100+
} catch (err) {
101+
return ["", newHostError(err)]
102+
}
103+
}
81104
return ["", ErrUnimplemented]
82105
}
83106

84107
export function TempDir(): string {
85-
return "/tmp"
108+
const value = pickFirstEnv(["TMPDIR", "TEMP", "TMP"])
109+
if (value !== "") {
110+
return value
111+
}
112+
return getPlatform() === "win32" ? "." : "/tmp"
86113
}
87114

88115
export function UserCacheDir(): [string, $.GoError] {
89-
return ["", ErrUnimplemented]
116+
if (getPlatform() === "win32") {
117+
const value = pickFirstEnv(["LOCALAPPDATA", "APPDATA", "USERPROFILE"])
118+
if (value !== "") {
119+
return [value, null]
120+
}
121+
return ["", ErrUnimplemented]
122+
}
123+
const value = pickFirstEnv(["XDG_CACHE_HOME"])
124+
if (value !== "") {
125+
return [value, null]
126+
}
127+
const [home, err] = UserHomeDir()
128+
if (err !== null || home === "") {
129+
return ["", err ?? ErrUnimplemented]
130+
}
131+
return [home.replace(/\/+$/, "") + "/.cache", null]
90132
}
91133

92134
export function UserConfigDir(): [string, $.GoError] {
93-
return ["", ErrUnimplemented]
135+
if (getPlatform() === "win32") {
136+
const value = pickFirstEnv(["APPDATA", "USERPROFILE"])
137+
if (value !== "") {
138+
return [value, null]
139+
}
140+
return ["", ErrUnimplemented]
141+
}
142+
const value = pickFirstEnv(["XDG_CONFIG_HOME"])
143+
if (value !== "") {
144+
return [value, null]
145+
}
146+
const [home, err] = UserHomeDir()
147+
if (err !== null || home === "") {
148+
return ["", err ?? ErrUnimplemented]
149+
}
150+
return [home.replace(/\/+$/, "") + "/.config", null]
94151
}
95152

96153
export function UserHomeDir(): [string, $.GoError] {
154+
const keys = getPlatform() === "win32" ? ["USERPROFILE", "HOMEDRIVE", "HOME"] : ["HOME"]
155+
const value = pickFirstEnv(keys)
156+
if (value !== "") {
157+
return [value, null]
158+
}
97159
return ["", ErrUnimplemented]
98-
}
160+
}

0 commit comments

Comments
 (0)