Skip to content

Commit 14c0530

Browse files
committed
Initial import project
0 parents  commit 14c0530

11 files changed

+1017
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
test.xml

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[![](https://goreportcard.com/badge/linuxsuren/cobra-extension)](https://goreportcard.com/report/linuxsuren/cobra-extension)
2+
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](https://godoc.org/github.com/linuxsuren/cobra-extension)
3+
[![Contributors](https://img.shields.io/github/contributors/linuxsuren/cobra-extension.svg)](https://github.com/linuxsuren/cobra-extension/graphs/contributors)
4+
[![GitHub release](https://img.shields.io/github/release/linuxsuren/cobra-extension.svg?label=release)](https://github.com/linuxsuren/cobra-extension/releases/latest)
5+
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/linuxsuren/cobra-extension)
6+
[![HitCount](http://hits.dwyl.com/linuxsuren/cobra-extension.svg)](http://hits.dwyl.com/linuxsuren/cobra-extension)
7+
8+
This project aims to provide an easy way to let you writing a plugin for your CLI project.
9+
10+
# Get started
11+
12+
`go get github.com/linuxsuren/cobra-extension`

go.mod

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/linuxsuren/cobra-extension
2+
3+
go 1.15
4+
5+
require (
6+
github.com/golang/mock v1.4.4
7+
github.com/onsi/ginkgo v1.14.2
8+
github.com/onsi/gomega v1.10.3
9+
github.com/spf13/cobra v1.1.1
10+
gopkg.in/yaml.v2 v2.4.0
11+
)

go.sum

+344
Large diffs are not rendered by default.

output.go

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package pkg
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"github.com/spf13/cobra"
7+
"gopkg.in/yaml.v2"
8+
"io"
9+
"reflect"
10+
"strings"
11+
)
12+
13+
// OutputOption represent the format of output
14+
type OutputOption struct {
15+
Format string
16+
17+
Columns string
18+
WithoutHeaders bool
19+
Filter []string
20+
21+
Writer io.Writer
22+
CellRenderMap map[string]RenderCell
23+
}
24+
25+
// RenderCell render a specific cell in a table
26+
type RenderCell = func(string) string
27+
28+
// FormatOutput is the interface of format output
29+
type FormatOutput interface {
30+
Output(obj interface{}, format string) (data []byte, err error)
31+
}
32+
33+
const (
34+
// JSONOutputFormat is the format of json
35+
JSONOutputFormat string = "json"
36+
// YAMLOutputFormat is the format of yaml
37+
YAMLOutputFormat string = "yaml"
38+
// TableOutputFormat is the format of table
39+
TableOutputFormat string = "table"
40+
)
41+
42+
// Output print the object into byte array
43+
// Deprecated see also OutputV2
44+
func (o *OutputOption) Output(obj interface{}) (data []byte, err error) {
45+
switch o.Format {
46+
case JSONOutputFormat:
47+
return json.MarshalIndent(obj, "", " ")
48+
case YAMLOutputFormat:
49+
return yaml.Marshal(obj)
50+
}
51+
52+
return nil, fmt.Errorf("not support format %s", o.Format)
53+
}
54+
55+
// OutputV2 print the data line by line
56+
func (o *OutputOption) OutputV2(obj interface{}) (err error) {
57+
if o.Writer == nil {
58+
err = fmt.Errorf("no writer found")
59+
return
60+
}
61+
62+
if len(o.Columns) == 0 {
63+
err = fmt.Errorf("no columns found")
64+
return
65+
}
66+
67+
//cmd.logger.Debug("start to output", zap.Any("filter", o.Filter))
68+
obj = o.ListFilter(obj)
69+
70+
var data []byte
71+
switch o.Format {
72+
case JSONOutputFormat:
73+
data, err = json.MarshalIndent(obj, "", " ")
74+
case YAMLOutputFormat:
75+
data, err = yaml.Marshal(obj)
76+
case TableOutputFormat, "":
77+
table := CreateTableWithHeader(o.Writer, o.WithoutHeaders)
78+
table.AddHeader(strings.Split(o.Columns, ",")...)
79+
items := reflect.ValueOf(obj)
80+
for i := 0; i < items.Len(); i++ {
81+
table.AddRow(o.GetLine(items.Index(i))...)
82+
}
83+
table.Render()
84+
default:
85+
err = fmt.Errorf("not support format %s", o.Format)
86+
}
87+
88+
if err == nil && len(data) > 0 {
89+
_, err = o.Writer.Write(data)
90+
}
91+
return
92+
}
93+
94+
// ListFilter filter the data list by fields
95+
func (o *OutputOption) ListFilter(obj interface{}) interface{} {
96+
if len(o.Filter) == 0 {
97+
return obj
98+
}
99+
100+
elemType := reflect.TypeOf(obj).Elem()
101+
elemSlice := reflect.MakeSlice(reflect.SliceOf(elemType), 0, 10)
102+
items := reflect.ValueOf(obj)
103+
for i := 0; i < items.Len(); i++ {
104+
item := items.Index(i)
105+
if o.Match(item) {
106+
elemSlice = reflect.Append(elemSlice, item)
107+
}
108+
}
109+
return elemSlice.Interface()
110+
}
111+
112+
// Match filter an item
113+
func (o *OutputOption) Match(item reflect.Value) bool {
114+
for _, f := range o.Filter {
115+
arr := strings.Split(f, "=")
116+
if len(arr) < 2 {
117+
continue
118+
}
119+
120+
key := arr[0]
121+
val := arr[1]
122+
123+
if !strings.Contains(ReflectFieldValueAsString(item, key), val) {
124+
return false
125+
}
126+
}
127+
return true
128+
}
129+
130+
// GetLine returns the line of a table
131+
func (o *OutputOption) GetLine(obj reflect.Value) []string {
132+
columns := strings.Split(o.Columns, ",")
133+
values := make([]string, 0)
134+
135+
if o.CellRenderMap == nil {
136+
o.CellRenderMap = make(map[string]RenderCell, 0)
137+
}
138+
139+
for _, col := range columns {
140+
cell := ReflectFieldValueAsString(obj, col)
141+
if renderCell, ok := o.CellRenderMap[col]; ok && renderCell != nil {
142+
cell = renderCell(cell)
143+
}
144+
145+
values = append(values, cell)
146+
}
147+
return values
148+
}
149+
150+
// SetFlag set flag of output format
151+
// Deprecated, see also SetFlagWithHeaders
152+
func (o *OutputOption) SetFlag(cmd *cobra.Command) {
153+
cmd.Flags().StringVarP(&o.Format, "output", "o", TableOutputFormat,
154+
"Format the output, supported formats: table, json, yaml")
155+
cmd.Flags().BoolVarP(&o.WithoutHeaders, "no-headers", "", false,
156+
`When using the default output format, don't print headers (default print headers)`)
157+
cmd.Flags().StringArrayVarP(&o.Filter, "filter", "", []string{},
158+
"Filter for the list by fields")
159+
}
160+
161+
// SetFlagWithHeaders set the flags of output
162+
func (o *OutputOption) SetFlagWithHeaders(cmd *cobra.Command, headers string) {
163+
o.SetFlag(cmd)
164+
cmd.Flags().StringVarP(&o.Columns, "columns", "", headers,
165+
"The columns of table")
166+
}

padding.go

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package pkg
2+
3+
import (
4+
"math"
5+
"strings"
6+
"unicode"
7+
"unicode/utf8"
8+
)
9+
10+
const (
11+
// AlignLeft align left
12+
AlignLeft = 0
13+
// AlignCenter align center
14+
AlignCenter = 1
15+
// AlignRight align right
16+
AlignRight = 2
17+
)
18+
19+
// Pad give a pad
20+
func Pad(s, pad string, width int, align int) string {
21+
switch align {
22+
case AlignCenter:
23+
return PadCenter(s, pad, width)
24+
case AlignRight:
25+
return PadLeft(s, pad, width)
26+
default:
27+
return PadRight(s, pad, width)
28+
}
29+
}
30+
31+
// PadRight pas as right
32+
func PadRight(s, pad string, width int) string {
33+
gap := widthValue(s, width)
34+
if gap > 0 {
35+
return s + strings.Repeat(string(pad), gap)
36+
}
37+
return s
38+
}
39+
40+
// PadLeft pad as left
41+
func PadLeft(s, pad string, width int) string {
42+
gap := widthValue(s, width)
43+
if gap > 0 {
44+
return strings.Repeat(string(pad), gap) + s
45+
}
46+
return s
47+
}
48+
49+
// PadCenter pad as center
50+
func PadCenter(s, pad string, width int) string {
51+
gap := widthValue(s, width)
52+
if gap > 0 {
53+
gapLeft := int(math.Ceil(float64(gap / 2)))
54+
gapRight := gap - gapLeft
55+
return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight)
56+
}
57+
return s
58+
}
59+
60+
func isHan(s string) (isHan bool) {
61+
wh := []rune(s)
62+
for _, r := range wh {
63+
if unicode.Is(unicode.Han, r) {
64+
isHan = true
65+
} else if unicode.Is(unicode.Hiragana, r) {
66+
isHan = true
67+
} else if unicode.Is(unicode.Katakana, r) {
68+
isHan = true
69+
} else if unicode.Is(unicode.Common, r) {
70+
isHan = true
71+
} else {
72+
isHan = false
73+
break
74+
}
75+
}
76+
return
77+
}
78+
79+
func countCN(s string) (count int) {
80+
wh := []rune(s)
81+
for _, r := range wh {
82+
if unicode.Is(unicode.Han, r) {
83+
count++
84+
} else if unicode.Is(unicode.Hiragana, r) {
85+
count++
86+
} else if unicode.Is(unicode.Katakana, r) {
87+
count++
88+
} else if unicode.Is(unicode.Common, r) && len(string(r)) != 1 {
89+
count++
90+
}
91+
}
92+
return
93+
}
94+
95+
func widthValue(s string, width int) (gap int) {
96+
l := utf8.RuneCountInString(s)
97+
ln := len(s)
98+
isHan := isHan(s)
99+
count := countCN(s)
100+
if ln != l {
101+
if isHan {
102+
gap = width - (ln - l)
103+
} else {
104+
gap = width - (ln - count)
105+
}
106+
} else {
107+
gap = width - l
108+
}
109+
return
110+
}
111+
112+
// Lenf counts the number
113+
func Lenf(han string) (l int) {
114+
ln := len(han)
115+
l = utf8.RuneCountInString(han)
116+
isHan := isHan(han)
117+
count := countCN(han)
118+
if ln != l {
119+
if isHan {
120+
l = ln - l
121+
} else {
122+
l = ln - count
123+
}
124+
125+
}
126+
return
127+
}

0 commit comments

Comments
 (0)