From c925c16855d47027791cb19d3f40442add27d9b1 Mon Sep 17 00:00:00 2001 From: Jesse Bouwman Date: Wed, 18 Aug 2021 19:17:12 -0700 Subject: [PATCH] Work in progress: experimentation with console output Several open issues mention problems with interaction of the global `--encoding=` flag and the Encoders and PostRun fields of command structs. This branch contains experimental refactors that explore approaches to consistent command execution patterns across offline, online and http modes. Specific tickets: - https://github.com/ipfs/go-ipfs/issues/7050 json encoding for `ls` - https://github.com/ipfs/go-ipfs/issues/1121 json encoding for `add` - https://github.com/ipfs/go-ipfs/issues/5594 json encoding for `stats bw` - https://github.com/ipfs/go-ipfs-cmds/issues/115 postrun design Possibly related: - https://github.com/ipfs/go-ipfs/issues/6640 global flags on subcommands Incomplete PRs: - https://github.com/ipfs/go-ipfs/pull/5620 json for 'stat' --- cli/parse.go | 10 ---------- cli/run.go | 10 ---------- command.go | 9 +++++++++ executor.go | 16 ++++++++++++++-- http/client.go | 6 ++++++ 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/cli/parse.go b/cli/parse.go index eebd6b43..e8926759 100644 --- a/cli/parse.go +++ b/cli/parse.go @@ -45,16 +45,6 @@ func Parse(ctx context.Context, input []string, stdin *os.File, root *cmds.Comma return req, err } - // if no encoding was specified by user, default to plaintext encoding - // (if command doesn't support plaintext, use JSON instead) - if enc := req.Options[cmds.EncLong]; enc == "" { - if req.Command.Encoders != nil && req.Command.Encoders[cmds.Text] != nil { - req.SetOption(cmds.EncLong, cmds.Text) - } else { - req.SetOption(cmds.EncLong, cmds.JSON) - } - } - return req, nil } diff --git a/cli/run.go b/cli/run.go index 46b68280..f57c29a6 100644 --- a/cli/run.go +++ b/cli/run.go @@ -102,8 +102,6 @@ func Run(ctx context.Context, root *cmds.Command, return nil } - cmd := req.Command - env, err := buildEnv(req.Context, req) if err != nil { printErr(err) @@ -119,14 +117,6 @@ func Run(ctx context.Context, root *cmds.Command, return err } - encTypeStr, _ := req.Options[cmds.EncLong].(string) - encType := cmds.EncodingType(encTypeStr) - - // use JSON if text was requested but the command doesn't have a text-encoder - if _, ok := cmd.Encoders[encType]; encType == cmds.Text && !ok { - req.Options[cmds.EncLong] = cmds.JSON - } - re, err := NewResponseEmitter(stdout, stderr, req) if err != nil { printErr(err) diff --git a/command.go b/command.go index f31b3de2..0937eb8e 100644 --- a/command.go +++ b/command.go @@ -11,6 +11,7 @@ package cmds import ( "errors" "fmt" + "io" "strings" files "github.com/ipfs/go-ipfs-files" @@ -30,6 +31,10 @@ type Function func(*Request, ResponseEmitter, Environment) error // PostRunMap is the map used in Command.PostRun. type PostRunMap map[PostRunType]func(Response, ResponseEmitter) error +// DisplayCLI supplies an function for console output in local +// processes in combination with a text Encoder. +type DisplayCLI func(res Response, stdout, stderr io.Writer) error + // Command is a runnable command, with input arguments and options (flags). // It can also have Subcommands, to group units of work into sets. type Command struct { @@ -62,6 +67,10 @@ type Command struct { // the local process. PostRun PostRunMap + // DisplayCLI is for text output in local process in + // combination with 'text' Encoder. + DisplayCLI DisplayCLI + // Encoders encode results from Run (and/or PostRun) in the desired // encoding. Encoders EncoderMap diff --git a/executor.go b/executor.go index f25f5e68..209c03eb 100644 --- a/executor.go +++ b/executor.go @@ -2,6 +2,7 @@ package cmds import ( "context" + "os" ) type Executor interface { @@ -51,9 +52,20 @@ func (x *executor) Execute(req *Request, re ResponseEmitter, env Environment) er } } - // contains the error returned by PostRun + // contains the error returned by DisplayCLI or PostRun errCh := make(chan error, 1) - if cmd.PostRun != nil { + if cmd.DisplayCLI != nil && GetEncoding(req, "json") == "text" { + var ( + res Response + ) + + re, res = NewChanResponsePair(req) + + go func() { + defer close(errCh) + errCh <- cmd.DisplayCLI(res, os.Stdout, os.Stderr) + }() + } else if cmd.PostRun != nil { if typer, ok := re.(interface { Type() PostRunType }); ok && cmd.PostRun[typer.Type()] != nil { diff --git a/http/client.go b/http/client.go index d19c9fb9..0a0589e4 100644 --- a/http/client.go +++ b/http/client.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "net/url" + "os" "strings" "github.com/ipfs/go-ipfs-cmds" @@ -132,6 +133,11 @@ func (c *client) Execute(req *cmds.Request, re cmds.ResponseEmitter, env cmds.En } } + if cmd.DisplayCLI != nil && + cmds.GetEncoding(req, cmds.Undefined) == cmds.Text { + return cmd.DisplayCLI(res, os.Stdout, os.Stderr) + } + return cmds.Copy(re, res) }