From 59f4994ec56506dd42f23a352fc3a448f53dc620 Mon Sep 17 00:00:00 2001 From: Ian Thomas Date: Fri, 24 May 2024 12:12:32 +0100 Subject: [PATCH] Command output to file --- src/file_system.ts | 2 ++ src/io/file_output.ts | 6 +++++- src/jupyter_file_system.ts | 23 ++++++++++++++++------- src/shell.ts | 22 ++++++++++++++++++---- tests/shell.test.ts | 19 +++++++++++++++++++ 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/file_system.ts b/src/file_system.ts index 30b7d22..9b4a6aa 100644 --- a/src/file_system.ts +++ b/src/file_system.ts @@ -10,4 +10,6 @@ export interface IFileSystem { // Assume new file ... touch(path: string): Promise + + write(path: string, content: string): Promise } diff --git a/src/io/file_output.ts b/src/io/file_output.ts index 57f24c9..ae4464f 100644 --- a/src/io/file_output.ts +++ b/src/io/file_output.ts @@ -7,11 +7,15 @@ export class FileOutput extends BufferedOutput { this.fs = fs this.path = path this.append = append + + if (this.append) { + throw Error("FileOutput in append mode not implemented") + } } override async flush(): Promise { const all_data = this.data.join() - console.log("TO FILE:", this.fs, this.path, this.append, all_data) + this.fs.write(this.path, all_data); this.clear() } diff --git a/src/jupyter_file_system.ts b/src/jupyter_file_system.ts index 99b2306..a5f8a74 100644 --- a/src/jupyter_file_system.ts +++ b/src/jupyter_file_system.ts @@ -18,13 +18,18 @@ export class JupyterFileSystem implements IFileSystem { } async list(path: string): Promise { - const listing = await this._contentsManager.get(path, { content: true }) - if (listing.type == "file") { - return [listing.name] - } else { // listing.type == "directory" - const content = listing.content as Contents.IModel[] - const filenames = content.map((model) => model.name) - return filenames.sort() + try { + const listing = await this._contentsManager.get(path, { content: true }) + if (listing.type == "file") { + return [listing.name] + } else { // listing.type == "directory" + const content = listing.content as Contents.IModel[] + const filenames = content.map((model) => model.name) + return filenames.sort() + } + } catch (error: any) { + // Need to handle possible error cases here. + return [] } } @@ -34,5 +39,9 @@ export class JupyterFileSystem implements IFileSystem { await this._contentsManager.rename(model.path, path) } + async write(path: string, content: string): Promise { + await this._contentsManager.save(path, { content }) + } + private _contentsManager: Contents.IManager } diff --git a/src/shell.ts b/src/shell.ts index ae11c65..a062098 100644 --- a/src/shell.ts +++ b/src/shell.ts @@ -1,6 +1,6 @@ import { CommandRegistry } from "./command_registry" import { Context } from "./context" -import { TerminalOutput } from "./io" +import { FileOutput, Output, TerminalOutput } from "./io" import { OutputCallback } from "./output_callback" import { CommandNode, parse } from "./parse" import { IFileSystem } from "./file_system" @@ -105,11 +105,25 @@ export class Shell { throw new Error(`Unknown command: '${cmdName}'`) } + let output: Output = stdout + if (cmd.redirects) { + // Support single redirect only, write (not append) to file. + if (cmd.redirects.length > 1) { + throw Error("Only implemented a single redirect per command") + } + if (cmd.redirects[0].token.value != ">") { + throw Error("Only implemented redirect write to file") + } + + const path = cmd.redirects[0].target.value + output = new FileOutput(this._filesystem, path, false) + } + const cmdArgs = cmd.suffix.map((token) => token.value) - const context = new Context(cmdArgs, this._filesystem, stdout, this._env) - //const exit_code = await command?.run(context) + const context = new Context(cmdArgs, this._filesystem, output, this._env) + //const exit_code = await command?.run(context) // Do something with exit_code await command?.run(context) - await stdout.flush() + await output.flush() } } catch (error: any) { // Send result via output?????? With color. Should be to stderr. diff --git a/tests/shell.test.ts b/tests/shell.test.ts index b67c818..4d29881 100644 --- a/tests/shell.test.ts +++ b/tests/shell.test.ts @@ -28,6 +28,25 @@ describe("Shell", () => { await shell._runCommands("env") expect(output.text).toEqual("PS1=\x1b[1;31mjs-shell:$\x1b[1;0m \r\nPWD=/\r\nCOLUMNS=0\r\nLINES=0\r\n") }) + + it("should echo to terminal", async () => { + const fs = await file_system_setup("jupyter") + const output = new MockTerminalOutput() + const shell = new Shell(fs, output.callback) + await shell._runCommands("echo sometext") + expect(output.text).toEqual("sometext\r\n") + }) + + it("should echo to file", async () => { + const fs = await file_system_setup("jupyter") + const output = new MockTerminalOutput() + const shell = new Shell(fs, output.callback) + expect(await fs.list("afile.txt")).toEqual([]) + await shell._runCommands("echo sometext > afile.txt") + expect(await fs.list("afile.txt")).toEqual(["afile.txt"]) + // Should this really have appended \r\n ??? + expect(await fs.get("afile.txt")).toEqual("sometext\r\n") + }) }) describe("input", () => {