-
Notifications
You must be signed in to change notification settings - Fork 1
/
streams.go
155 lines (131 loc) · 3.75 KB
/
streams.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Package ioes or "in, out, error streams" provides standard names and utilities
// for working with traditional stdin/stdout/stderr streams.
package ioes
import (
"bytes"
"io"
"io/ioutil"
"os"
"time"
"github.com/briandowns/spinner"
"github.com/mattn/go-isatty"
)
// IOStreams provides standard names for iostreams and common methods for managing
// progress indicators & other contextual output. IOStreams is broken out as a
// sepearate package to facilitate plumbing contextual feedback into the depths
// of an application architecture, while also providing clear state management
// for when contextual feedback doesn't work. As an example, IOStreams may be
// routed over websockets to provide HTTP output, and in this context it's far
// easier to disable things like spinners outright
// IOStreams must be created with a "New" method
type IOStreams struct {
In io.Reader
Out io.Writer
ErrOut io.Writer
sp *spinner.Spinner
}
// IsTerminal returns true when IOStreams Out is a terminal file descriptor
func (s IOStreams) IsTerminal() bool {
if osOutFile, ok := s.Out.(*os.File); ok {
if osOutFile == os.Stdout {
return isatty.IsTerminal(osOutFile.Fd())
}
}
return false
}
// IsCygwinTerminal returns true when IOStreams Out is a Cygwin file descriptor
func (s IOStreams) IsCygwinTerminal() bool {
if osOutFile, ok := s.Out.(*os.File); ok {
if osOutFile == os.Stdout {
return isatty.IsCygwinTerminal(osOutFile.Fd())
}
}
return false
}
// StartSpinner begins the progress spinner
func (s IOStreams) StartSpinner() {
s.sp.Start()
}
// StopSpinner halts the progress spinner
func (s IOStreams) StopSpinner() {
s.sp.Stop()
}
// SpinnerActive returns the active state of the progres spinner
func (s IOStreams) SpinnerActive() bool {
return s.sp.Active()
}
// SpinnerMsg sets the spinner suffix message
func (s IOStreams) SpinnerMsg(msg string) {
s.sp.Suffix = msg
}
// Close checks to see if any/all of in/out/errOut are closable,
// and if so closes them
func (s IOStreams) Close() error {
// TODO
return nil
}
// Print writes a msg to the out stream, printing
func (s IOStreams) Print(msg string) {
if s.SpinnerActive() {
s.StopSpinner()
defer s.StartSpinner()
}
s.Out.Write([]byte(msg))
}
// PrintErr writes a msg to the Err stream, printing
func (s IOStreams) PrintErr(msg string) {
if s.SpinnerActive() {
s.StopSpinner()
defer s.StartSpinner()
}
s.ErrOut.Write([]byte(msg))
}
// NewIOStreams creates streams
func NewIOStreams(in io.Reader, out, errOut io.Writer) IOStreams {
sp := spinner.New(spinner.CharSets[24], 100*time.Millisecond)
sp.Writer = errOut
return IOStreams{
In: in,
Out: out,
ErrOut: errOut,
sp: sp,
}
}
// NewStdIOStreams creates a standard set of streams, with in, out, and error mapped
// to os.Stdin, os.Stdout, and os.Stderr respectively
func NewStdIOStreams() IOStreams {
sp := spinner.New(spinner.CharSets[24], 100*time.Millisecond)
sp.Writer = os.Stderr
return IOStreams{
In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr,
sp: sp,
}
}
// NewTestIOStreams returns a valid IOStreams and in, out, errout buffers for unit tests
func NewTestIOStreams() (IOStreams, *bytes.Buffer, *bytes.Buffer, *bytes.Buffer) {
in := &bytes.Buffer{}
out := &bytes.Buffer{}
errOut := &bytes.Buffer{}
sp := spinner.New(spinner.CharSets[24], 100*time.Millisecond)
sp.Writer = ioutil.Discard
return IOStreams{
In: in,
Out: out,
ErrOut: errOut,
sp: sp,
}, in, out, errOut
}
// NewDiscardIOStreams returns a valid IOStreams that just discards
func NewDiscardIOStreams() IOStreams {
in := &bytes.Buffer{}
sp := spinner.New(spinner.CharSets[24], 100*time.Millisecond)
sp.Writer = ioutil.Discard
return IOStreams{
In: in,
Out: ioutil.Discard,
ErrOut: ioutil.Discard,
sp: sp,
}
}