Skip to content

Commit

Permalink
Initial import project
Browse files Browse the repository at this point in the history
  • Loading branch information
LinuxSuRen committed Dec 4, 2020
0 parents commit 14c0530
Show file tree
Hide file tree
Showing 11 changed files with 1,017 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
test.xml
12 changes: 12 additions & 0 deletions README.md
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`
11 changes: 11 additions & 0 deletions go.mod
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
)
344 changes: 344 additions & 0 deletions go.sum

Large diffs are not rendered by default.

166 changes: 166 additions & 0 deletions output.go
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")
}
127 changes: 127 additions & 0 deletions padding.go
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
}
Loading

0 comments on commit 14c0530

Please sign in to comment.