Skip to content

Commit fba3137

Browse files
committed
Added new experimental 'output' module to replace formatter
1 parent 6b23384 commit fba3137

File tree

5 files changed

+327
-0
lines changed

5 files changed

+327
-0
lines changed

Gopkg.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

commands/root/root.go

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"io/ioutil"
2222
"os"
2323

24+
"github.com/arduino/arduino-cli/output"
25+
2426
"golang.org/x/crypto/ssh/terminal"
2527

2628
"github.com/mattn/go-colorable"
@@ -96,6 +98,10 @@ func preRun(cmd *cobra.Command, args []string) {
9698
commands.GlobalFlags.Format = "text"
9799
}
98100
formatter.SetFormatter(commands.GlobalFlags.Format)
101+
if commands.GlobalFlags.Format != "text" {
102+
output.SetOutputKind(output.JSON)
103+
}
104+
99105
logrus.Info("Formatter set")
100106
if !formatter.IsCurrentFormat("text") {
101107
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {

output/output.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* Copyright 2018 ARDUINO SA (http://www.arduino.cc/)
5+
*
6+
* This software is released under the GNU General Public License version 3,
7+
* which covers the main part of arduino-cli.
8+
* The terms of this license can be found at:
9+
* https://www.gnu.org/licenses/gpl-3.0.en.html
10+
*
11+
* You can be released from the requirements of the above licenses by purchasing
12+
* a commercial license. Buying such a license is mandatory if you want to modify or
13+
* otherwise use the software for commercial activities involving the Arduino
14+
* software without disclosing the source code of your own applications. To purchase
15+
* a commercial license, send an email to [email protected].
16+
*/
17+
18+
package output
19+
20+
import (
21+
"fmt"
22+
23+
colorable "github.com/mattn/go-colorable"
24+
)
25+
26+
var colorStdout = colorable.NewColorableStdout()
27+
28+
type Output interface {
29+
EmitTerminal() string
30+
EmitJSON() string
31+
}
32+
33+
type OutputKind int
34+
35+
const (
36+
Terminal OutputKind = iota
37+
JSON
38+
)
39+
40+
var defaultOutputKind = Terminal
41+
42+
func SetOutputKind(kind OutputKind) {
43+
defaultOutputKind = kind
44+
}
45+
46+
func Emit(data Output) {
47+
switch defaultOutputKind {
48+
case Terminal:
49+
fmt.Fprint(colorStdout, data.EmitTerminal())
50+
case JSON:
51+
fmt.Print(data.EmitJSON())
52+
}
53+
}

output/table.go

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* Copyright 2018 ARDUINO SA (http://www.arduino.cc/)
5+
*
6+
* This software is released under the GNU General Public License version 3,
7+
* which covers the main part of arduino-cli.
8+
* The terms of this license can be found at:
9+
* https://www.gnu.org/licenses/gpl-3.0.en.html
10+
*
11+
* You can be released from the requirements of the above licenses by purchasing
12+
* a commercial license. Buying such a license is mandatory if you want to modify or
13+
* otherwise use the software for commercial activities involving the Arduino
14+
* software without disclosing the source code of your own applications. To purchase
15+
* a commercial license, send an email to [email protected].
16+
*/
17+
18+
package output
19+
20+
import (
21+
"fmt"
22+
"math"
23+
)
24+
25+
type Table struct {
26+
hasHeader bool
27+
columnsCount int
28+
columnsWidthMode []TableColumnWidthMode
29+
rows []*TableRow
30+
}
31+
32+
type TableRow struct {
33+
cells []TextBox
34+
}
35+
36+
func NewTable() *Table {
37+
return &Table{
38+
rows: []*TableRow{},
39+
}
40+
}
41+
42+
type TableColumnWidthMode int
43+
44+
const (
45+
Minimum TableColumnWidthMode = iota
46+
Average
47+
)
48+
49+
func (t *Table) SetColumnWidthMode(x int, mode TableColumnWidthMode) {
50+
for len(t.columnsWidthMode) <= x {
51+
t.columnsWidthMode = append(t.columnsWidthMode, Minimum)
52+
}
53+
t.columnsWidthMode[x] = mode
54+
}
55+
56+
func (t *Table) makeTableRow(columns ...interface{}) *TableRow {
57+
columnsCount := len(columns)
58+
if t.columnsCount < columnsCount {
59+
t.columnsCount = columnsCount
60+
}
61+
cells := make([]TextBox, columnsCount)
62+
for i, col := range columns {
63+
switch text := col.(type) {
64+
case TextBox:
65+
cells[i] = text
66+
case string:
67+
cells[i] = Sprintf("%s", text)
68+
case fmt.Stringer:
69+
cells[i] = Sprintf("%s", text.String())
70+
default:
71+
panic(fmt.Sprintf("invalid column argument type: %t", col))
72+
}
73+
}
74+
return &TableRow{cells: cells}
75+
}
76+
77+
func (t *Table) SetHeader(columns ...interface{}) {
78+
row := t.makeTableRow(columns...)
79+
if t.hasHeader {
80+
t.rows[0] = row
81+
} else {
82+
t.rows = append([]*TableRow{row}, t.rows...)
83+
t.hasHeader = true
84+
}
85+
}
86+
87+
func (t *Table) AddRow(columns ...interface{}) {
88+
row := t.makeTableRow(columns...)
89+
t.rows = append(t.rows, row)
90+
}
91+
92+
func (t *Table) Render() string {
93+
// find max width for each row
94+
average := make([]int, t.columnsCount)
95+
widths := make([]int, t.columnsCount)
96+
count := make([]int, t.columnsCount)
97+
for _, row := range t.rows {
98+
for x, cell := range row.cells {
99+
l := cell.Len()
100+
if l == 0 {
101+
continue
102+
}
103+
count[x]++
104+
average[x] += l
105+
if cell.Len() > widths[x] {
106+
widths[x] = l
107+
}
108+
}
109+
}
110+
for x := range average {
111+
average[x] = average[x] / count[x]
112+
}
113+
// fmt.Println("COUNT:", count)
114+
// fmt.Println("MAX :", widths)
115+
// fmt.Println("AVG :", average)
116+
variance := make([]int, t.columnsCount)
117+
for _, row := range t.rows {
118+
for x, cell := range row.cells {
119+
l := cell.Len()
120+
if l == 0 {
121+
continue
122+
}
123+
d := l - average[x]
124+
variance[x] += d * d
125+
}
126+
}
127+
for x := range variance {
128+
variance[x] = int(math.Sqrt(float64(variance[x] / count[x])))
129+
}
130+
// fmt.Println("VAR :", variance)
131+
132+
res := ""
133+
for _, row := range t.rows {
134+
separator := ""
135+
for x, cell := range row.cells {
136+
selectedWidth := widths[x]
137+
if x < len(t.columnsWidthMode) {
138+
switch t.columnsWidthMode[x] {
139+
case Minimum:
140+
selectedWidth = widths[x]
141+
case Average:
142+
selectedWidth = average[x] + variance[x]*2
143+
}
144+
}
145+
res += separator
146+
res += cell.Pad(selectedWidth)
147+
separator = " "
148+
}
149+
res += "\n"
150+
}
151+
return res
152+
}

output/text.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* Copyright 2018 ARDUINO SA (http://www.arduino.cc/)
5+
*
6+
* This software is released under the GNU General Public License version 3,
7+
* which covers the main part of arduino-cli.
8+
* The terms of this license can be found at:
9+
* https://www.gnu.org/licenses/gpl-3.0.en.html
10+
*
11+
* You can be released from the requirements of the above licenses by purchasing
12+
* a commercial license. Buying such a license is mandatory if you want to modify or
13+
* otherwise use the software for commercial activities involving the Arduino
14+
* software without disclosing the source code of your own applications. To purchase
15+
* a commercial license, send an email to [email protected].
16+
*/
17+
18+
package output
19+
20+
import (
21+
"fmt"
22+
23+
"github.com/fatih/color"
24+
)
25+
26+
var red = color.New(color.FgRed).SprintfFunc()
27+
var blue = color.New(color.FgBlue).SprintfFunc()
28+
var green = color.New(color.FgGreen).SprintfFunc()
29+
var yellow = color.New(color.FgYellow).SprintfFunc()
30+
31+
func Red(in string) *Text {
32+
return &Text{raw: red(in), clean: in}
33+
}
34+
35+
func Blue(in string) *Text {
36+
return &Text{raw: blue(in), clean: in}
37+
}
38+
39+
func Green(in string) *Text {
40+
return &Text{raw: green(in), clean: in}
41+
}
42+
43+
func Yellow(in string) *Text {
44+
return &Text{raw: yellow(in), clean: in}
45+
}
46+
47+
type TextBox interface {
48+
Len() int
49+
Pad(availableWidth int) string
50+
}
51+
52+
type Text struct {
53+
clean string
54+
raw string
55+
justify int
56+
}
57+
58+
func (t *Text) Len() int {
59+
return len(t.clean)
60+
}
61+
62+
// func (t *Text) String() string {
63+
// return t.raw
64+
// }
65+
66+
func (t *Text) JustifyLeft() {
67+
t.justify = 0
68+
}
69+
70+
func (t *Text) JustifyCenter() {
71+
t.justify = 1
72+
}
73+
74+
func (t *Text) JustifyRight() {
75+
t.justify = 2
76+
}
77+
78+
func (t *Text) Pad(totalLen int) string {
79+
delta := totalLen - t.Len()
80+
switch t.justify {
81+
case 0:
82+
return t.raw + spaces(delta)
83+
case 1:
84+
return spaces(delta/2) + t.raw + spaces(delta-delta/2)
85+
case 2:
86+
return spaces(delta) + t.raw
87+
}
88+
panic(fmt.Sprintf("internal error: invalid justify %d", t.justify))
89+
}
90+
91+
func spaces(n int) string {
92+
res := ""
93+
for n > 0 {
94+
res += " "
95+
n--
96+
}
97+
return res
98+
}
99+
100+
func Sprintf(format string, args ...interface{}) TextBox {
101+
cleanArgs := make([]interface{}, len(args))
102+
for i, arg := range args {
103+
if text, ok := arg.(*Text); ok {
104+
cleanArgs[i], args[i] = text.clean, text.raw
105+
} else {
106+
cleanArgs[i] = args[i]
107+
}
108+
}
109+
110+
return &Text{
111+
clean: fmt.Sprintf(format, cleanArgs...),
112+
raw: fmt.Sprintf(format, args...),
113+
}
114+
}

0 commit comments

Comments
 (0)