From a20123fc24bf5f0ab94fdf4dd1eaff182a04278e Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 27 Apr 2024 21:03:54 -0400 Subject: [PATCH] feat: allow hilbish.run to take a table of files to use for output (#291) --- CHANGELOG.md | 29 +++++++++ api.go | 118 ++++++++++++++++++++++++++++++---- cmd/docgen/docgen.go | 6 +- docs/api/commander.md | 7 +- docs/api/fs.md | 17 +++++ docs/api/hilbish/_index.md | 13 ++-- emmyLuaDocs/fs.lua | 4 ++ emmyLuaDocs/hilbish.lua | 7 +- exec.go | 40 ++++++++---- go.mod | 24 +++---- go.sum | 51 ++++++--------- golibs/commander/commander.go | 7 +- golibs/fs/fs.go | 18 ++++++ 13 files changed, 261 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2253549..021edf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # 🎀 Changelog +## Unreleased +### Added +- `fs.pipe` function to get a pair of connected files (a pipe). +- Added an alternative 2nd parameter to `hilbish.run`, which is `streams`. +`streams` is a table of input and output streams to run the command with. +It uses these 3 keys: + - `input` as standard input for the command + - `out` as standard output + - `err` as standard error + +Here is a minimal example of the new usage which allows users to now pipe commands +directly via Lua functions: + +```lua +local fs = require 'fs' +local pr, pw = fs.pipe() +hilbish.run('ls -l', { + stdout = pw, + stderr = pw, +}) + +pw:close() + +hilbish.run('wc -l', { + stdin = pr +}) +``` + ## [2.2.3] - 2024-04-27 ### Fixed - Highligher and hinter work now, since it was regressed from the previous minor release. @@ -716,6 +744,7 @@ This input for example will prompt for more input to complete: First "stable" release of Hilbish. +[2.2.3]: https://github.com/Rosettea/Hilbish/compare/v2.2.2...v2.2.3 [2.2.2]: https://github.com/Rosettea/Hilbish/compare/v2.2.1...v2.2.2 [2.2.1]: https://github.com/Rosettea/Hilbish/compare/v2.2.0...v2.2.1 [2.2.0]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.2.0 diff --git a/api.go b/api.go index b8e62b3..6f8f517 100644 --- a/api.go +++ b/api.go @@ -16,6 +16,7 @@ import ( "bytes" "errors" "fmt" + "io" "os" "os/exec" "runtime" @@ -27,6 +28,7 @@ import ( rt "github.com/arnodel/golua/runtime" "github.com/arnodel/golua/lib/packagelib" + "github.com/arnodel/golua/lib/iolib" "github.com/maxlandon/readline" "mvdan.cc/sh/v3/interp" ) @@ -152,12 +154,64 @@ func unsetVimMode() { util.SetField(l, hshMod, "vimMode", rt.NilValue) } -// run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string) +func handleStream(v rt.Value, strms *streams, errStream bool) error { + ud, ok := v.TryUserData() + if !ok { + return errors.New("expected metatable argument") + } + + val := ud.Value() + var varstrm io.Writer + if f, ok := val.(*iolib.File); ok { + varstrm = f.Handle() + } + + if f, ok := val.(*sink); ok { + varstrm = f.writer + } + + if varstrm == nil { + return errors.New("expected either a sink or file") + } + + if errStream { + strms.stderr = varstrm + } else { + strms.stdout = varstrm + } + + return nil +} + +// run(cmd, streams) -> exitCode (number), stdout (string), stderr (string) // Runs `cmd` in Hilbish's shell script interpreter. +// The `streams` parameter specifies the output and input streams the command should use. +// For example, to write command output to a sink. +// As a table, the caller can directly specify the standard output, error, and input +// streams of the command with the table keys `out`, `err`, and `input` respectively. +// As a boolean, it specifies whether the command should use standard output or return its output streams. // #param cmd string -// #param returnOut boolean If this is true, the function will return the standard output and error of the command instead of printing it. +// #param streams table|boolean // #returns number, string, string +// #example +/* +// This code is the same as `ls -l | wc -l` +local fs = require 'fs' +local pr, pw = fs.pipe() +hilbish.run('ls -l', { + stdout = pw, + stderr = pw, +}) + +pw:close() + +hilbish.run('wc -l', { + stdin = pr +}) +*/ +// #example func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + // TODO: ON BREAKING RELEASE, DO NOT ACCEPT `streams` AS A BOOLEAN. if err := c.Check1Arg(); err != nil { return nil, err } @@ -166,20 +220,57 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return nil, err } + strms := &streams{} var terminalOut bool if len(c.Etc()) != 0 { tout := c.Etc()[0] - termOut, ok := tout.TryBool() - terminalOut = termOut + + var ok bool + terminalOut, ok = tout.TryBool() if !ok { - return nil, errors.New("bad argument to run (expected boolean, got " + tout.TypeName() + ")") + luastreams, ok := tout.TryTable() + if !ok { + return nil, errors.New("bad argument to run (expected boolean or table, got " + tout.TypeName() + ")") + } + + handleStream(luastreams.Get(rt.StringValue("out")), strms, false) + handleStream(luastreams.Get(rt.StringValue("err")), strms, true) + + stdinstrm := luastreams.Get(rt.StringValue("input")) + if !stdinstrm.IsNil() { + ud, ok := stdinstrm.TryUserData() + if !ok { + return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file, got " + stdinstrm.TypeName() + ")") + } + + val := ud.Value() + var varstrm io.Reader + if f, ok := val.(*iolib.File); ok { + varstrm = f.Handle() + } + + if f, ok := val.(*sink); ok { + varstrm = f.reader + } + + if varstrm == nil { + return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file)") + } + + strms.stdin = varstrm + } + } else { + if !terminalOut { + strms = &streams{ + stdout: new(bytes.Buffer), + stderr: new(bytes.Buffer), + } + } } - } else { - terminalOut = true } var exitcode uint8 - stdout, stderr, err := execCommand(cmd, terminalOut) + stdout, stderr, err := execCommand(cmd, strms) if code, ok := interp.IsExitStatus(err); ok { exitcode = code @@ -187,11 +278,12 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { exitcode = 1 } - stdoutStr := "" - stderrStr := "" - if !terminalOut { - stdoutStr = stdout.(*bytes.Buffer).String() - stderrStr = stderr.(*bytes.Buffer).String() + var stdoutStr, stderrStr string + if stdoutBuf, ok := stdout.(*bytes.Buffer); ok { + stdoutStr = stdoutBuf.String() + } + if stderrBuf, ok := stderr.(*bytes.Buffer); ok { + stderrStr = stderrBuf.String() } return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil diff --git a/cmd/docgen/docgen.go b/cmd/docgen/docgen.go index 86a622a..bf8fd1b 100644 --- a/cmd/docgen/docgen.go +++ b/cmd/docgen/docgen.go @@ -488,7 +488,11 @@ func main() { } mdTable.SetContent(i - diff, 0, fmt.Sprintf(`%s`, dps.FuncName, dps.FuncSig)) - mdTable.SetContent(i - diff, 1, dps.Doc[0]) + if len(dps.Doc) == 0 { + fmt.Printf("WARNING! Function %s on module %s has no documentation!\n", dps.FuncName, modname) + } else { + mdTable.SetContent(i - diff, 1, dps.Doc[0]) + } } f.WriteString(mdTable.String()) f.WriteString("\n") diff --git a/docs/api/commander.md b/docs/api/commander.md index 03ece54..c26445a 100644 --- a/docs/api/commander.md +++ b/docs/api/commander.md @@ -26,8 +26,11 @@ In this example, a command with the name of `hello` is created that will print `Hello world!` to output. One question you may have is: What is the `sinks` parameter? -The `sinks` parameter is a table with 3 keys: `in`, `out`, -and `err`. All of them are a Sink. +The `sinks` parameter is a table with 3 keys: `input`, `out`, and `err`. +There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`) +as `in` is also a Lua keyword, so `input` is preferred for use. +All of them are a Sink. +In the future, `sinks.in` will be removed. - `in` is the standard input. You may use the read functions on this sink to get input from the user. diff --git a/docs/api/fs.md b/docs/api/fs.md index bc14055..7b733ef 100644 --- a/docs/api/fs.md +++ b/docs/api/fs.md @@ -23,6 +23,7 @@ library offers more functions and will work on any operating system Hilbish does |glob(pattern) -> matches (table)|Match all files based on the provided `pattern`.| |join(...path) -> string|Takes any list of paths and joins them based on the operating system's path separator.| |mkdir(name, recursive)|Creates a new directory with the provided `name`.| +|fpipe() -> File, File|Returns a pair of connected files, also known as a pipe.| |readdir(path) -> table[string]|Returns a list of all files and directories in the provided path.| |stat(path) -> {}|Returns the information about a given `path`.| @@ -183,6 +184,22 @@ fs.mkdir('./foo/bar', true) ``` +
+
+

+fs.fpipe() -> File, File + + + +

+ +Returns a pair of connected files, also known as a pipe. +The type returned is a Lua file, same as returned from `io` functions. + +#### Parameters +This function has no parameters. +
+

diff --git a/docs/api/hilbish/_index.md b/docs/api/hilbish/_index.md index b79dcde..1407e69 100644 --- a/docs/api/hilbish/_index.md +++ b/docs/api/hilbish/_index.md @@ -28,7 +28,7 @@ interfaces and functions which directly relate to shell functionality. |prependPath(dir)|Prepends `dir` to $PATH.| |prompt(str, typ)|Changes the shell prompt to the provided string.| |read(prompt) -> input (string)|Read input from the user, using Hilbish's line editor/input reader.| -|run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)|Runs `cmd` in Hilbish's shell script interpreter.| +|run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)|Runs `cmd` in Hilbish's shell script interpreter.| |runnerMode(mode)|Sets the execution/runner mode for interactive Hilbish.| |timeout(cb, time) -> @Timer|Executed the `cb` function after a period of `time`.| |which(name) -> string|Checks if `name` is a valid command.| @@ -413,20 +413,25 @@ Text to print before input, can be empty.

-hilbish.run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string) +hilbish.run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)

Runs `cmd` in Hilbish's shell script interpreter. +Specifies the output and input streams the command should use. +For example, to write command output to a sink. +As a table, the caller can directly specify the standard output, error, and input +streams of the command with the table keys `out`, `err`, and `input` respectively. +As a boolean, it specifies whether the command should use standard output or return its output streams. #### Parameters `string` **`cmd`** -`boolean` **`returnOut`** -If this is true, the function will return the standard output and error of the command instead of printing it. +`table|boolean` **`streams`** +
diff --git a/emmyLuaDocs/fs.lua b/emmyLuaDocs/fs.lua index 89a418b..ef80eba 100644 --- a/emmyLuaDocs/fs.lua +++ b/emmyLuaDocs/fs.lua @@ -34,6 +34,10 @@ function fs.join(...path) end --- function fs.mkdir(name, recursive) end +--- Returns a pair of connected files, also known as a pipe. +--- The type returned is a Lua file, same as returned from `io` functions. +function fs.fpipe() end + --- Returns a list of all files and directories in the provided path. function fs.readdir(path) end diff --git a/emmyLuaDocs/hilbish.lua b/emmyLuaDocs/hilbish.lua index 7cca355..d931918 100644 --- a/emmyLuaDocs/hilbish.lua +++ b/emmyLuaDocs/hilbish.lua @@ -132,7 +132,12 @@ function hilbish.prompt(str, typ) end function hilbish.read(prompt) end --- Runs `cmd` in Hilbish's shell script interpreter. -function hilbish.run(cmd, returnOut) end +--- Specifies the output and input streams the command should use. +--- For example, to write command output to a sink. +--- As a table, the caller can directly specify the standard output, error, and input +--- streams of the command with the table keys `out`, `err`, and `input` respectively. +--- As a boolean, it specifies whether the command should use standard output or return its output streams. +function hilbish.run(cmd, streams) end --- Sets the execution/runner mode for interactive Hilbish. --- This determines whether Hilbish wll try to run input as Lua diff --git a/exec.go b/exec.go index cf84231..355fa3d 100644 --- a/exec.go +++ b/exec.go @@ -28,6 +28,12 @@ var errNotExec = errors.New("not executable") var errNotFound = errors.New("not found") var runnerMode rt.Value = rt.StringValue("hybrid") +type streams struct { + stdout io.Writer + stderr io.Writer + stdin io.Reader +} + type execError struct{ typ string cmd string @@ -236,7 +242,7 @@ func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr } func execSh(cmdString string) (string, uint8, bool, error) { - _, _, err := execCommand(cmdString, true) + _, _, err := execCommand(cmdString, nil) if err != nil { // If input is incomplete, start multiline prompting if syntax.IsIncomplete(err) { @@ -257,7 +263,7 @@ func execSh(cmdString string) (string, uint8, bool, error) { } // Run command in sh interpreter -func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { +func execCommand(cmd string, strms *streams) (io.Writer, io.Writer, error) { file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "") if err != nil { return nil, nil, err @@ -265,15 +271,24 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { runner, _ := interp.New() - var stdout io.Writer - var stderr io.Writer - if terminalOut { - interp.StdIO(os.Stdin, os.Stdout, os.Stderr)(runner) - } else { - stdout = new(bytes.Buffer) - stderr = new(bytes.Buffer) - interp.StdIO(os.Stdin, stdout, stderr)(runner) + if strms == nil { + strms = &streams{} } + + if strms.stdout == nil { + strms.stdout = os.Stdout + } + + if strms.stderr == nil { + strms.stderr = os.Stderr + } + + if strms.stdin == nil { + strms.stdin = os.Stdin + } + + interp.StdIO(strms.stdin, strms.stdout, strms.stderr)(runner) + buf := new(bytes.Buffer) printer := syntax.NewPrinter() @@ -292,11 +307,11 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { interp.ExecHandler(execHandle(bg))(runner) err = runner.Run(context.TODO(), stmt) if err != nil { - return stdout, stderr, err + return strms.stdout, strms.stderr, err } } - return stdout, stderr, nil + return strms.stdout, strms.stderr, nil } func execHandle(bg bool) interp.ExecHandlerFunc { @@ -334,6 +349,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc { sinks := rt.NewTable() sinks.Set(rt.StringValue("in"), rt.UserDataValue(stdin.ud)) + sinks.Set(rt.StringValue("input"), rt.UserDataValue(stdin.ud)) sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud)) sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud)) diff --git a/go.mod b/go.mod index 6753a17..a7975b7 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,26 @@ module hilbish go 1.18 require ( - github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86 + github.com/arnodel/golua v0.0.0-20230215163904-e0b5347eaaa1 github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 - github.com/blackfireio/osinfo v1.0.3 - github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036 + github.com/blackfireio/osinfo v1.0.5 + github.com/maxlandon/readline v1.0.14 github.com/pborman/getopt v1.1.0 - github.com/sahilm/fuzzy v0.1.0 - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a - golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 - mvdan.cc/sh/v3 v3.5.1 + github.com/sahilm/fuzzy v0.1.1 + golang.org/x/sys v0.19.0 + golang.org/x/term v0.19.0 + mvdan.cc/sh/v3 v3.8.0 ) require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/arnodel/strftime v0.1.6 // indirect - github.com/evilsocket/islazy v1.10.6 // indirect + github.com/evilsocket/islazy v1.11.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect - golang.org/x/text v0.3.7 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/text v0.14.0 // indirect ) replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b @@ -31,4 +31,4 @@ replace github.com/maxlandon/readline => ./readline replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10 -replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 +replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749 diff --git a/go.sum b/go.sum index f82ef01..193f17e 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,21 @@ -github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 h1:QNYjYDogUSiNUkffbhFSrSCtpZhofeiVYGFN2FI4wSs= -github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE= +github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749 h1:jIFnWBTsYw8s7RX7H2AOXjDVhWP3ol7OzUVaPN2KnGI= +github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= -github.com/arnodel/edit v0.0.0-20220202110212-dfc8d7a13890/go.mod h1:AcpttpuZBaL9xl8/CX+Em4fBTUbwIkJ66RiAsJlNrBk= github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw= github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 h1:R1/AOzdMbopSliUTTEHvHbyNmnZ3YxY5GvdhTkpPsSY= github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504/go.mod h1:kHBCvAXJIatTX1pw6tLiOspjGc3MhUDRlog9yrCUS+k= -github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c= -github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA= +github.com/blackfireio/osinfo v1.0.5 h1:6hlaWzfcpb87gRmznVf7wSdhysGqLRz9V/xuSdCEXrA= +github.com/blackfireio/osinfo v1.0.5/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc= github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo= -github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/evilsocket/islazy v1.11.0 h1:B5w6uuS6ki6iDG+aH/RFeoMb8ijQh/pGabewqp2UeJ0= +github.com/evilsocket/islazy v1.11.0/go.mod h1:muYH4x5MB5YRdkxnrOtrXLIBX6LySj1uFIqys94LKdo= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -30,40 +25,30 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0= github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0= github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= -golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0= diff --git a/golibs/commander/commander.go b/golibs/commander/commander.go index ea2da7a..f4d588d 100644 --- a/golibs/commander/commander.go +++ b/golibs/commander/commander.go @@ -17,8 +17,11 @@ In this example, a command with the name of `hello` is created that will print `Hello world!` to output. One question you may have is: What is the `sinks` parameter? -The `sinks` parameter is a table with 3 keys: `in`, `out`, -and `err`. All of them are a @Sink. +The `sinks` parameter is a table with 3 keys: `input`, `out`, and `err`. +There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`) +as `in` is also a Lua keyword, so `input` is preferred for use. +All of them are a @Sink. +In the future, `sinks.in` will be removed. - `in` is the standard input. You may use the read functions on this sink to get input from the user. diff --git a/golibs/fs/fs.go b/golibs/fs/fs.go index 5bd22c6..9e03325 100644 --- a/golibs/fs/fs.go +++ b/golibs/fs/fs.go @@ -18,6 +18,7 @@ import ( rt "github.com/arnodel/golua/runtime" "github.com/arnodel/golua/lib/packagelib" + "github.com/arnodel/golua/lib/iolib" ) var Loader = packagelib.Loader{ @@ -36,6 +37,7 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { "dir": util.LuaExport{fdir, 1, false}, "glob": util.LuaExport{fglob, 1, false}, "join": util.LuaExport{fjoin, 0, true}, + "pipe": util.LuaExport{fpipe, 0, false}, } mod := rt.NewTable() util.SetExports(rtm, mod, exports) @@ -226,6 +228,22 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), err } +// fpipe() -> File, File +// Returns a pair of connected files, also known as a pipe. +// The type returned is a Lua file, same as returned from `io` functions. +// #returns File +// #returns File +func fpipe(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + rf, wf, err := os.Pipe() + if err != nil { + return nil, err + } + + rfLua := iolib.NewFile(rf, 0) + wfLua := iolib.NewFile(wf, 0) + + return c.PushingNext(t.Runtime, rfLua.Value(t.Runtime), wfLua.Value(t.Runtime)), nil +} // readdir(path) -> table[string] // Returns a list of all files and directories in the provided path. // #param dir string