Add simple byte-counting export progress-bar
Peter Rabbitson committed
Mar 26, 2020 at 16:35 UTC
4772ca6134d77b77b59e60a36297badcdab3a4e8
1 file changed
+69
core/commands/dag/dag.go
+69
@@ -1,10 +1,13 @@
1
package dagcmd
2
3
import (
4
+ "errors"
5
"fmt"
6
"io"
7
"math"
8
+ "os"
9
"strings"
10
+ "time"
11
12
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
13
"github.com/ipfs/go-ipfs/core/coredag"
@@ -23,6 +26,12 @@ import (
26
//gipfree "github.com/ipld/go-ipld-prime/impl/free"
27
//gipselector "github.com/ipld/go-ipld-prime/traversal/selector"
28
//gipselectorbuilder "github.com/ipld/go-ipld-prime/traversal/selector/builder"
29
+
30
+ "gopkg.in/cheggaaa/pb.v1"
31
+)
32
+
33
+const (
34
+ progressOptionName = "progress"
35
)
36
37
var DagCmd = &cmds.Command{
@@ -261,6 +270,9 @@ The output of blocks happens in strict DAG-traversal, first-seen, order.
270
Arguments: []cmds.Argument{
271
cmds.StringArg("root", true, false, "CID of a root to recursively export").EnableStdin(),
272
},
273
+ Options: []cmds.Option{
274
+ cmds.BoolOption(progressOptionName, "p", "Display progress on CLI. Defaults to true when STDERR is a TTY."),
275
+ },
276
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
277
278
c, err := cid.Decode(req.Arguments[0])
@@ -334,4 +346,61 @@ The output of blocks happens in strict DAG-traversal, first-seen, order.
346
347
return err
348
},
349
+ PostRun: cmds.PostRunMap{
350
+ cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
351
+
352
+ var showProgress bool
353
+ val, specified := res.Request().Options[progressOptionName]
354
+ if !specified {
355
+ // default based on TTY availability
356
+ errStat, _ := os.Stderr.Stat()
357
+ if 0 != (errStat.Mode() & os.ModeCharDevice) {
358
+ showProgress = true
359
+ }
360
+ } else if val.(bool) {
361
+ showProgress = true
362
+ }
363
+
364
+ // simple passthrough, no progress
365
+ if !showProgress {
366
+ return cmds.Copy(re, res)
367
+ }
368
+
369
+ bar := pb.New64(0).SetUnits(pb.U_BYTES)
370
+ bar.Output = os.Stderr
371
+ bar.ShowSpeed = true
372
+ bar.ShowElapsedTime = true
373
+ bar.RefreshRate = 500 * time.Millisecond
374
+ bar.Start()
375
+
376
+ var processedOneResponse bool
377
+ for {
378
+ v, err := res.Next()
379
+ if err == io.EOF {
380
+
381
+ // We only write the final bar update on success
382
+ // On error it looks too weird
383
+ bar.Finish()
384
+
385
+ return re.Close()
386
+ } else if err != nil {
387
+ return re.CloseWithError(err)
388
+ } else if processedOneResponse {
389
+ return re.CloseWithError(errors.New("unexpected multipart response during emit, please file a bugreport"))
390
+ }
391
+
392
+ r, ok := v.(io.Reader)
393
+ if !ok {
394
+ // some sort of encoded response, this should not be happening
395
+ return errors.New("unexpected non-stream passed to PostRun: please file a bugreport")
396
+ }
397
+
398
+ processedOneResponse = true
399
+
400
+ if err := re.Emit(bar.NewProxyReader(r)); err != nil {
401
+ return err
402
+ }
403
+ }
404
+ },
405
+ },
406
}