-
-
Notifications
You must be signed in to change notification settings - Fork 398
/
Copy pathfeedback.go
168 lines (146 loc) · 4.52 KB
/
feedback.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
156
157
158
159
160
161
162
163
164
165
166
167
168
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].
package feedback
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"github.com/arduino/arduino-cli/i18n"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/status"
)
// OutputFormat is used to determine the output format
type OutputFormat int
const (
// Text means plain text format, suitable for ansi terminals
Text OutputFormat = iota
// JSON means JSON format
JSON
// JSONMini is identical to JSON but without whitespaces
JSONMini
)
// Result is anything more complex than a sentence that needs to be printed
// for the user.
type Result interface {
fmt.Stringer
Data() interface{}
}
// Feedback wraps an io.Writer and provides an uniform API the CLI can use to
// provide feedback to the users.
type Feedback struct {
out io.Writer
err io.Writer
format OutputFormat
}
var tr = i18n.Tr
// New creates a Feedback instance
func New(out, err io.Writer, format OutputFormat) *Feedback {
return &Feedback{
out: out,
err: err,
format: format,
}
}
// DefaultFeedback provides a basic feedback object to be used as default.
func DefaultFeedback() *Feedback {
return New(os.Stdout, os.Stderr, Text)
}
// SetOut can be used to change the out writer at runtime
func (fb *Feedback) SetOut(out io.Writer) {
fb.out = out
}
// SetErr can be used to change the err writer at runtime
func (fb *Feedback) SetErr(err io.Writer) {
fb.err = err
}
// SetFormat can be used to change the output format at runtime
func (fb *Feedback) SetFormat(f OutputFormat) {
fb.format = f
}
// GetFormat returns the output format currently set
func (fb *Feedback) GetFormat() OutputFormat {
return fb.format
}
// OutputWriter returns the underlying io.Writer to be used when the Print*
// api is not enough.
func (fb *Feedback) OutputWriter() io.Writer {
return fb.out
}
// ErrorWriter is the same as OutputWriter but exposes the underlying error
// writer.
func (fb *Feedback) ErrorWriter() io.Writer {
return fb.out
}
// Printf behaves like fmt.Printf but writes on the out writer and adds a newline.
func (fb *Feedback) Printf(format string, v ...interface{}) {
fb.Print(fmt.Sprintf(format, v...))
}
// Print behaves like fmt.Print but writes on the out writer and adds a newline.
func (fb *Feedback) Print(v interface{}) {
if fb.format == JSON || fb.format == JSONMini {
fb.printJSON(v)
} else {
fmt.Fprintln(fb.out, v)
}
}
// Errorf behaves like fmt.Printf but writes on the error writer and adds a
// newline. It also logs the error.
func (fb *Feedback) Errorf(format string, v ...interface{}) {
// Unbox grpc status errors
for i := range v {
if s, isStatus := v[i].(*status.Status); isStatus {
v[i] = errors.New(s.Message())
} else if err, isErr := v[i].(error); isErr {
if s, isStatus := status.FromError(err); isStatus {
v[i] = errors.New(s.Message())
}
}
}
fb.Error(fmt.Sprintf(format, v...))
}
// Error behaves like fmt.Print but writes on the error writer and adds a
// newline. It also logs the error.
func (fb *Feedback) Error(v ...interface{}) {
fmt.Fprintln(fb.err, v...)
logrus.Error(fmt.Sprint(v...))
}
// printJSON is a convenient wrapper to provide feedback by printing the
// desired output in a pretty JSON format. It adds a newline to the output.
func (fb *Feedback) printJSON(v interface{}) {
var d []byte
var err error
if fb.format == JSON {
d, err = json.MarshalIndent(v, "", " ")
} else if fb.format == JSONMini {
d, err = json.Marshal(v)
}
if err != nil {
fb.Errorf(tr("Error during JSON encoding of the output: %v"), err)
} else {
fmt.Fprintf(fb.out, "%v\n", string(d))
}
}
// PrintResult is a convenient wrapper to provide feedback for complex data,
// where the contents can't be just serialized to JSON but requires more
// structure.
func (fb *Feedback) PrintResult(res Result) {
if fb.format == JSON || fb.format == JSONMini {
fb.printJSON(res.Data())
} else {
fb.Print(fmt.Sprintf("%s", res))
}
}