Skip to content

Commit

Permalink
allow setting mode and ownership by reference, add move and replace m…
Browse files Browse the repository at this point in the history
…ethods
  • Loading branch information
joshuaboud committed Jun 3, 2024
1 parent 3d896fe commit 7c051d1
Showing 1 changed file with 154 additions and 18 deletions.
172 changes: 154 additions & 18 deletions houston-common-lib/lib/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,8 +580,20 @@ export class FileSystemNode extends Path {
setMode(
mode: Mode | number,
commandOptions?: CommandOptions
): ResultAsync<this, ProcessError>;
setMode(
reference: FileSystemNode,
commandOptions?: CommandOptions
): ResultAsync<this, ProcessError>;
setMode(
mode: Mode | number | FileSystemNode,
commandOptions?: CommandOptions
): ResultAsync<this, ProcessError> {
return this.setModeOn(this.server, mode, commandOptions);
return (
mode instanceof FileSystemNode
? mode.getMode(commandOptions)
: okAsync<Mode | number, ProcessError>(mode)
).andThen((mode) => this.setModeOn(this.server, mode, commandOptions));
}

getOwnership(
Expand All @@ -593,8 +605,22 @@ export class FileSystemNode extends Path {
setOwnership(
ownership: Ownership,
commandOptions?: CommandOptions | undefined
): ResultAsync<this, ProcessError>;
setOwnership(
reference: FileSystemNode,
commandOptions?: CommandOptions | undefined
): ResultAsync<this, ProcessError>;
setOwnership(
ownership: Ownership | FileSystemNode,
commandOptions?: CommandOptions | undefined
): ResultAsync<this, ProcessError> {
return this.setOwnershipOn(this.server, ownership, commandOptions);
return (
ownership instanceof FileSystemNode
? ownership.getOwnership(commandOptions)
: okAsync(ownership)
).andThen((ownership) =>
this.setOwnershipOn(this.server, ownership, commandOptions)
);
}

assertExists(
Expand Down Expand Up @@ -681,6 +707,38 @@ export class FileSystemNode extends Path {
commandOptions
);
}

move(
newPath: string,
commandOptions: CommandOptions & {
existing?: "fail" | "clobber" | "backup";
} = {}
): ResultAsync<FileSystemNode, ProcessError> {
const { existing, ...options }: typeof commandOptions = {
existing: "fail",
...commandOptions,
};
const existingFlagsLUT: Record<typeof existing, string[]> = {
fail: ["--no-clobber"],
clobber: ["--force"],
backup: ["--backup=numbered"],
};
return this.server
.execute(
new Command(
[
"mv",
...existingFlagsLUT[existing],
"--no-target-directory",
"--",
this.path,
newPath,
],
options
)
)
.map(() => new FileSystemNode(this.server, newPath));
}
}

export class File extends FileSystemNode {
Expand All @@ -691,26 +749,24 @@ export class File extends FileSystemNode {
return this.createOn(this.server, "file", parents, commandOptions);
}

remove(commandOptions?: CommandOptions): ResultAsync<null, ProcessError> {
remove(commandOptions?: CommandOptions): ResultAsync<this, ProcessError> {
return this.server
.execute(new Command(["rm", this.path], commandOptions), true)
.map(() => null);
.map(() => this);
}

read(
binary?: false,
commandOptions?: CommandOptions
commandOptions?: CommandOptions & { binary?: false }
): ResultAsync<string, ProcessError>;
read(
binary: true,
commandOptions?: CommandOptions
commandOptions: CommandOptions & { binary: true }
): ResultAsync<Uint8Array, ProcessError>;
read(
binary: boolean = false,
commandOptions?: CommandOptions
commandOptions: CommandOptions & { binary?: boolean } = {}
): ResultAsync<string, ProcessError> | ResultAsync<Uint8Array, ProcessError> {
const { binary, ...options } = commandOptions;
const procResult = this.server.execute(
new Command(["cat", this.path], commandOptions)
new Command(["cat", this.path], options)
);
if (binary) {
return procResult.map((p) => p.getStdout(true));
Expand All @@ -720,21 +776,95 @@ export class File extends FileSystemNode {

write(
content: string | Uint8Array,
append: boolean = false,
commandOptions?: CommandOptions
): ResultAsync<null, ProcessError> {
commandOptions: CommandOptions & { append?: boolean } = {}
): ResultAsync<this, ProcessError> {
const { append, ...options } = commandOptions;
const proc = this.server.spawnProcess(
new Command(
[
"dd",
`of=${this.path}`,
...(append ? ["oflag=append", "conv=notrunc"] : []),
],
commandOptions
options
)
);
proc.write(content, false);
return proc.wait(true).map(() => null);
return proc.wait(true).map(() => this);
}

move(
...args: Parameters<FileSystemNode["move"]>
): ResultAsync<File, ProcessError> {
return super.move(...args).map((node) => new File(node));
}

replace(
newContent: string | Uint8Array,
commandOptions?: CommandOptions & { backup?: boolean }
): ResultAsync<this, ProcessError>;
replace(
replacer: (oldContent: string) => string,
commandOptions?: CommandOptions & { binary?: false; backup?: boolean }
): ResultAsync<this, ProcessError>;
replace(
replacer: (oldContent: Uint8Array) => Uint8Array,
commandOptions: CommandOptions & { binary: true; backup?: boolean }
): ResultAsync<this, ProcessError>;
replace(
newContentOrReplacer:
| string
| Uint8Array
| ((oldContent: string) => string)
| ((oldContent: Uint8Array) => Uint8Array),
commandOptions: CommandOptions & { binary?: boolean; backup?: boolean } = {}
): ResultAsync<this, ProcessError> {
const { binary, backup, ...options } = commandOptions;
return File.makeTemp(this.server, this.parent().path, options)
.andThen((tempFile) => {
if (typeof newContentOrReplacer === "function") {
if (binary) {
return this.read({ ...options, binary: true })
.map(
newContentOrReplacer as (oldContent: Uint8Array) => Uint8Array
)
.andThen((newContent) => tempFile.write(newContent, options));
}
return this.read({ ...options, binary: false })
.map(newContentOrReplacer as (oldContent: string) => string)
.andThen((newContent) => tempFile.write(newContent, options));
}
return okAsync(newContentOrReplacer).andThen((newContent) =>
tempFile.write(newContent, options)
);
})
.andThen((tempFile) => tempFile.setOwnership(this, options))
.andThen((tempFile) => tempFile.setMode(this, options))
.andThen((tempFile) =>
tempFile.move(this.path, {
...options,
existing: backup ? "backup" : "clobber",
})
)
.map(() => this);
}

static makeTemp(
server: Server,
directory?: string,
commandOptions?: CommandOptions
): ResultAsync<File, ProcessError> {
return server
.execute(
new Command(
[
"mktemp",
...(directory === undefined ? [] : [`--tmpdir=${directory}`]),
],
commandOptions
)
)
.map((proc) => new File(server, proc.getStdout().trim()));
}
}

Expand All @@ -746,10 +876,10 @@ export class Directory extends FileSystemNode {
return this.createOn(this.server, "directory", parents, commandOptions);
}

remove(commandOptions?: CommandOptions): ResultAsync<null, ProcessError> {
remove(commandOptions?: CommandOptions): ResultAsync<this, ProcessError> {
return this.server
.execute(new Command(["rmdir", this.path], commandOptions), true)
.map(() => null);
.map(() => this);
}

getChildren(
Expand Down Expand Up @@ -791,4 +921,10 @@ export class Directory extends FileSystemNode {
.map((pathString) => new FileSystemNode(this.server, pathString))
);
}

move(
...args: Parameters<FileSystemNode["move"]>
): ResultAsync<Directory, ProcessError> {
return super.move(...args).map((node) => new Directory(node));
}
}

0 comments on commit 7c051d1

Please sign in to comment.