-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 14c0530
Showing
11 changed files
with
1,017 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.idea | ||
test.xml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[![](https://goreportcard.com/badge/linuxsuren/cobra-extension)](https://goreportcard.com/report/linuxsuren/cobra-extension) | ||
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](https://godoc.org/github.com/linuxsuren/cobra-extension) | ||
[![Contributors](https://img.shields.io/github/contributors/linuxsuren/cobra-extension.svg)](https://github.com/linuxsuren/cobra-extension/graphs/contributors) | ||
[![GitHub release](https://img.shields.io/github/release/linuxsuren/cobra-extension.svg?label=release)](https://github.com/linuxsuren/cobra-extension/releases/latest) | ||
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/linuxsuren/cobra-extension) | ||
[![HitCount](http://hits.dwyl.com/linuxsuren/cobra-extension.svg)](http://hits.dwyl.com/linuxsuren/cobra-extension) | ||
|
||
This project aims to provide an easy way to let you writing a plugin for your CLI project. | ||
|
||
# Get started | ||
|
||
`go get github.com/linuxsuren/cobra-extension` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module github.com/linuxsuren/cobra-extension | ||
|
||
go 1.15 | ||
|
||
require ( | ||
github.com/golang/mock v1.4.4 | ||
github.com/onsi/ginkgo v1.14.2 | ||
github.com/onsi/gomega v1.10.3 | ||
github.com/spf13/cobra v1.1.1 | ||
gopkg.in/yaml.v2 v2.4.0 | ||
) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package pkg | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/spf13/cobra" | ||
"gopkg.in/yaml.v2" | ||
"io" | ||
"reflect" | ||
"strings" | ||
) | ||
|
||
// OutputOption represent the format of output | ||
type OutputOption struct { | ||
Format string | ||
|
||
Columns string | ||
WithoutHeaders bool | ||
Filter []string | ||
|
||
Writer io.Writer | ||
CellRenderMap map[string]RenderCell | ||
} | ||
|
||
// RenderCell render a specific cell in a table | ||
type RenderCell = func(string) string | ||
|
||
// FormatOutput is the interface of format output | ||
type FormatOutput interface { | ||
Output(obj interface{}, format string) (data []byte, err error) | ||
} | ||
|
||
const ( | ||
// JSONOutputFormat is the format of json | ||
JSONOutputFormat string = "json" | ||
// YAMLOutputFormat is the format of yaml | ||
YAMLOutputFormat string = "yaml" | ||
// TableOutputFormat is the format of table | ||
TableOutputFormat string = "table" | ||
) | ||
|
||
// Output print the object into byte array | ||
// Deprecated see also OutputV2 | ||
func (o *OutputOption) Output(obj interface{}) (data []byte, err error) { | ||
switch o.Format { | ||
case JSONOutputFormat: | ||
return json.MarshalIndent(obj, "", " ") | ||
case YAMLOutputFormat: | ||
return yaml.Marshal(obj) | ||
} | ||
|
||
return nil, fmt.Errorf("not support format %s", o.Format) | ||
} | ||
|
||
// OutputV2 print the data line by line | ||
func (o *OutputOption) OutputV2(obj interface{}) (err error) { | ||
if o.Writer == nil { | ||
err = fmt.Errorf("no writer found") | ||
return | ||
} | ||
|
||
if len(o.Columns) == 0 { | ||
err = fmt.Errorf("no columns found") | ||
return | ||
} | ||
|
||
//cmd.logger.Debug("start to output", zap.Any("filter", o.Filter)) | ||
obj = o.ListFilter(obj) | ||
|
||
var data []byte | ||
switch o.Format { | ||
case JSONOutputFormat: | ||
data, err = json.MarshalIndent(obj, "", " ") | ||
case YAMLOutputFormat: | ||
data, err = yaml.Marshal(obj) | ||
case TableOutputFormat, "": | ||
table := CreateTableWithHeader(o.Writer, o.WithoutHeaders) | ||
table.AddHeader(strings.Split(o.Columns, ",")...) | ||
items := reflect.ValueOf(obj) | ||
for i := 0; i < items.Len(); i++ { | ||
table.AddRow(o.GetLine(items.Index(i))...) | ||
} | ||
table.Render() | ||
default: | ||
err = fmt.Errorf("not support format %s", o.Format) | ||
} | ||
|
||
if err == nil && len(data) > 0 { | ||
_, err = o.Writer.Write(data) | ||
} | ||
return | ||
} | ||
|
||
// ListFilter filter the data list by fields | ||
func (o *OutputOption) ListFilter(obj interface{}) interface{} { | ||
if len(o.Filter) == 0 { | ||
return obj | ||
} | ||
|
||
elemType := reflect.TypeOf(obj).Elem() | ||
elemSlice := reflect.MakeSlice(reflect.SliceOf(elemType), 0, 10) | ||
items := reflect.ValueOf(obj) | ||
for i := 0; i < items.Len(); i++ { | ||
item := items.Index(i) | ||
if o.Match(item) { | ||
elemSlice = reflect.Append(elemSlice, item) | ||
} | ||
} | ||
return elemSlice.Interface() | ||
} | ||
|
||
// Match filter an item | ||
func (o *OutputOption) Match(item reflect.Value) bool { | ||
for _, f := range o.Filter { | ||
arr := strings.Split(f, "=") | ||
if len(arr) < 2 { | ||
continue | ||
} | ||
|
||
key := arr[0] | ||
val := arr[1] | ||
|
||
if !strings.Contains(ReflectFieldValueAsString(item, key), val) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// GetLine returns the line of a table | ||
func (o *OutputOption) GetLine(obj reflect.Value) []string { | ||
columns := strings.Split(o.Columns, ",") | ||
values := make([]string, 0) | ||
|
||
if o.CellRenderMap == nil { | ||
o.CellRenderMap = make(map[string]RenderCell, 0) | ||
} | ||
|
||
for _, col := range columns { | ||
cell := ReflectFieldValueAsString(obj, col) | ||
if renderCell, ok := o.CellRenderMap[col]; ok && renderCell != nil { | ||
cell = renderCell(cell) | ||
} | ||
|
||
values = append(values, cell) | ||
} | ||
return values | ||
} | ||
|
||
// SetFlag set flag of output format | ||
// Deprecated, see also SetFlagWithHeaders | ||
func (o *OutputOption) SetFlag(cmd *cobra.Command) { | ||
cmd.Flags().StringVarP(&o.Format, "output", "o", TableOutputFormat, | ||
"Format the output, supported formats: table, json, yaml") | ||
cmd.Flags().BoolVarP(&o.WithoutHeaders, "no-headers", "", false, | ||
`When using the default output format, don't print headers (default print headers)`) | ||
cmd.Flags().StringArrayVarP(&o.Filter, "filter", "", []string{}, | ||
"Filter for the list by fields") | ||
} | ||
|
||
// SetFlagWithHeaders set the flags of output | ||
func (o *OutputOption) SetFlagWithHeaders(cmd *cobra.Command, headers string) { | ||
o.SetFlag(cmd) | ||
cmd.Flags().StringVarP(&o.Columns, "columns", "", headers, | ||
"The columns of table") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package pkg | ||
|
||
import ( | ||
"math" | ||
"strings" | ||
"unicode" | ||
"unicode/utf8" | ||
) | ||
|
||
const ( | ||
// AlignLeft align left | ||
AlignLeft = 0 | ||
// AlignCenter align center | ||
AlignCenter = 1 | ||
// AlignRight align right | ||
AlignRight = 2 | ||
) | ||
|
||
// Pad give a pad | ||
func Pad(s, pad string, width int, align int) string { | ||
switch align { | ||
case AlignCenter: | ||
return PadCenter(s, pad, width) | ||
case AlignRight: | ||
return PadLeft(s, pad, width) | ||
default: | ||
return PadRight(s, pad, width) | ||
} | ||
} | ||
|
||
// PadRight pas as right | ||
func PadRight(s, pad string, width int) string { | ||
gap := widthValue(s, width) | ||
if gap > 0 { | ||
return s + strings.Repeat(string(pad), gap) | ||
} | ||
return s | ||
} | ||
|
||
// PadLeft pad as left | ||
func PadLeft(s, pad string, width int) string { | ||
gap := widthValue(s, width) | ||
if gap > 0 { | ||
return strings.Repeat(string(pad), gap) + s | ||
} | ||
return s | ||
} | ||
|
||
// PadCenter pad as center | ||
func PadCenter(s, pad string, width int) string { | ||
gap := widthValue(s, width) | ||
if gap > 0 { | ||
gapLeft := int(math.Ceil(float64(gap / 2))) | ||
gapRight := gap - gapLeft | ||
return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight) | ||
} | ||
return s | ||
} | ||
|
||
func isHan(s string) (isHan bool) { | ||
wh := []rune(s) | ||
for _, r := range wh { | ||
if unicode.Is(unicode.Han, r) { | ||
isHan = true | ||
} else if unicode.Is(unicode.Hiragana, r) { | ||
isHan = true | ||
} else if unicode.Is(unicode.Katakana, r) { | ||
isHan = true | ||
} else if unicode.Is(unicode.Common, r) { | ||
isHan = true | ||
} else { | ||
isHan = false | ||
break | ||
} | ||
} | ||
return | ||
} | ||
|
||
func countCN(s string) (count int) { | ||
wh := []rune(s) | ||
for _, r := range wh { | ||
if unicode.Is(unicode.Han, r) { | ||
count++ | ||
} else if unicode.Is(unicode.Hiragana, r) { | ||
count++ | ||
} else if unicode.Is(unicode.Katakana, r) { | ||
count++ | ||
} else if unicode.Is(unicode.Common, r) && len(string(r)) != 1 { | ||
count++ | ||
} | ||
} | ||
return | ||
} | ||
|
||
func widthValue(s string, width int) (gap int) { | ||
l := utf8.RuneCountInString(s) | ||
ln := len(s) | ||
isHan := isHan(s) | ||
count := countCN(s) | ||
if ln != l { | ||
if isHan { | ||
gap = width - (ln - l) | ||
} else { | ||
gap = width - (ln - count) | ||
} | ||
} else { | ||
gap = width - l | ||
} | ||
return | ||
} | ||
|
||
// Lenf counts the number | ||
func Lenf(han string) (l int) { | ||
ln := len(han) | ||
l = utf8.RuneCountInString(han) | ||
isHan := isHan(han) | ||
count := countCN(han) | ||
if ln != l { | ||
if isHan { | ||
l = ln - l | ||
} else { | ||
l = ln - count | ||
} | ||
|
||
} | ||
return | ||
} |
Oops, something went wrong.