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
|
package execenv
import (
"encoding/json"
"fmt"
"io"
"os"
"github.com/mattn/go-isatty"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/repository"
)
const RootCommandName = "git-bug"
const gitBugNamespace = "git-bug"
// Env is the environment of a command
type Env struct {
Repo repository.ClockedRepo
Backend *cache.RepoCache
In In
Out Out
Err Out
}
func NewEnv() *Env {
return &Env{
Repo: nil,
In: in{Reader: os.Stdin},
Out: out{Writer: os.Stdout},
Err: out{Writer: os.Stderr},
}
}
type In interface {
io.Reader
// IsTerminal tells if the input is a user terminal (rather than a buffer,
// a pipe ...), which tells if we can use interactive features.
IsTerminal() bool
// ForceIsTerminal allow to force the returned value of IsTerminal
// This only works in test scenario.
ForceIsTerminal(value bool)
}
type Out interface {
io.Writer
Printf(format string, a ...interface{})
Print(a ...interface{})
Println(a ...interface{})
PrintJSON(v interface{}) error
// IsTerminal tells if the output is a user terminal (rather than a buffer,
// a pipe ...), which tells if we can use colors and other interactive features.
IsTerminal() bool
// Raw return the underlying io.Writer, or itself if not.
// This is useful if something need to access the raw file descriptor.
Raw() io.Writer
// String returns what have been written in the output before, as a string.
// This only works in test scenario.
String() string
// Bytes returns what have been written in the output before, as []byte.
// This only works in test scenario.
Bytes() []byte
// Reset clear what has been recorded as written in the output before.
// This only works in test scenario.
Reset()
// ForceIsTerminal allow to force the returned value of IsTerminal
// This only works in test scenario.
ForceIsTerminal(value bool)
}
type in struct {
io.Reader
}
func (i in) IsTerminal() bool {
if f, ok := i.Reader.(*os.File); ok {
return isTerminal(f)
}
return false
}
func (i in) ForceIsTerminal(_ bool) {
panic("only work with a test env")
}
type out struct {
io.Writer
}
func (o out) Printf(format string, a ...interface{}) {
_, _ = fmt.Fprintf(o, format, a...)
}
func (o out) Print(a ...interface{}) {
_, _ = fmt.Fprint(o, a...)
}
func (o out) Println(a ...interface{}) {
_, _ = fmt.Fprintln(o, a...)
}
func (o out) PrintJSON(v interface{}) error {
raw, err := json.MarshalIndent(v, "", " ")
if err != nil {
return err
}
o.Println(string(raw))
return nil
}
func (o out) IsTerminal() bool {
if f, ok := o.Writer.(*os.File); ok {
return isTerminal(f)
}
return false
}
func (o out) Raw() io.Writer {
return o.Writer
}
func (o out) String() string {
panic("only work with a test env")
}
func (o out) Bytes() []byte {
panic("only work with a test env")
}
func (o out) Reset() {
panic("only work with a test env")
}
func (o out) ForceIsTerminal(_ bool) {
panic("only work with a test env")
}
func isTerminal(file *os.File) bool {
return isatty.IsTerminal(file.Fd()) || isatty.IsCygwinTerminal(file.Fd())
}
|