2023-04-23 17:57:36 +08:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2024-04-29 15:17:23 +08:00
|
|
|
"git.nspix.com/golang/kos/util/arrays"
|
2023-04-23 17:57:36 +08:00
|
|
|
"git.nspix.com/golang/kos/util/pool"
|
|
|
|
"github.com/mattn/go-runewidth"
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func isNormalKind(kind reflect.Kind) bool {
|
|
|
|
normalKinds := []reflect.Kind{
|
|
|
|
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int,
|
|
|
|
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint,
|
|
|
|
reflect.Float32, reflect.Float64,
|
|
|
|
reflect.String,
|
|
|
|
}
|
|
|
|
for _, k := range normalKinds {
|
|
|
|
if k == kind {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func serializeMap(val map[any]any) ([]byte, error) {
|
|
|
|
var (
|
|
|
|
canFormat bool
|
|
|
|
width int
|
|
|
|
maxWidth int
|
|
|
|
)
|
|
|
|
canFormat = true
|
|
|
|
for k, v := range val {
|
|
|
|
if !isNormalKind(reflect.Indirect(reflect.ValueOf(k)).Kind()) || !isNormalKind(reflect.Indirect(reflect.ValueOf(v)).Kind()) {
|
|
|
|
canFormat = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !canFormat {
|
|
|
|
return json.MarshalIndent(val, "", "\t")
|
|
|
|
}
|
|
|
|
ms := make(map[string]string)
|
|
|
|
for k, v := range val {
|
|
|
|
sk := fmt.Sprint(k)
|
|
|
|
ms[sk] = fmt.Sprint(v)
|
|
|
|
width = runewidth.StringWidth(sk)
|
|
|
|
if width > maxWidth {
|
|
|
|
maxWidth = width
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer := pool.GetBuffer()
|
|
|
|
defer pool.PutBuffer(buffer)
|
|
|
|
for k, v := range ms {
|
|
|
|
buffer.WriteString(fmt.Sprintf("%-"+strconv.Itoa(maxWidth+4)+"s %s\n", k, v))
|
|
|
|
}
|
|
|
|
return buffer.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func printBorder(w *bytes.Buffer, ws []int) {
|
|
|
|
for _, l := range ws {
|
|
|
|
w.WriteString("+")
|
|
|
|
w.WriteString(strings.Repeat("-", l+2))
|
|
|
|
}
|
|
|
|
w.WriteString("+\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
func toString(v any) string {
|
|
|
|
switch t := v.(type) {
|
|
|
|
case float32, float64:
|
|
|
|
return fmt.Sprintf("%.2f", t)
|
|
|
|
case time.Time:
|
|
|
|
return t.Format("2006-01-02 15:04:05")
|
|
|
|
default:
|
|
|
|
return fmt.Sprint(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printArray(vals [][]any) (buf []byte) {
|
|
|
|
var (
|
|
|
|
cell string
|
|
|
|
str string
|
|
|
|
widths []int
|
|
|
|
maxLength int
|
|
|
|
width int
|
|
|
|
rows [][]string
|
|
|
|
)
|
|
|
|
rows = make([][]string, 0, len(vals))
|
|
|
|
for _, value := range vals {
|
|
|
|
if len(value) > maxLength {
|
|
|
|
maxLength = len(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
widths = make([]int, maxLength)
|
|
|
|
for _, vs := range vals {
|
|
|
|
rl := len(vs)
|
|
|
|
row := make([]string, rl)
|
|
|
|
for i, val := range vs {
|
|
|
|
str = toString(val)
|
|
|
|
if rl > 1 {
|
|
|
|
width = runewidth.StringWidth(str)
|
|
|
|
if width > widths[i] {
|
|
|
|
widths[i] = width
|
|
|
|
}
|
|
|
|
}
|
|
|
|
row[i] = str
|
|
|
|
}
|
|
|
|
rows = append(rows, row)
|
|
|
|
}
|
|
|
|
buffer := pool.GetBuffer()
|
|
|
|
defer pool.PutBuffer(buffer)
|
|
|
|
printBorder(buffer, widths)
|
|
|
|
for index, row := range rows {
|
|
|
|
size := len(row)
|
|
|
|
for i, w := range widths {
|
|
|
|
cell = ""
|
|
|
|
buffer.WriteString("|")
|
|
|
|
if size > i {
|
|
|
|
cell = row[i]
|
|
|
|
}
|
|
|
|
buffer.WriteString(" ")
|
|
|
|
buffer.WriteString(cell)
|
|
|
|
cl := runewidth.StringWidth(cell)
|
|
|
|
if w > cl {
|
|
|
|
buffer.WriteString(strings.Repeat(" ", w-cl))
|
|
|
|
}
|
|
|
|
buffer.WriteString(" ")
|
|
|
|
}
|
|
|
|
buffer.WriteString("|\n")
|
|
|
|
if index == 0 {
|
|
|
|
printBorder(buffer, widths)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printBorder(buffer, widths)
|
|
|
|
return buffer.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
func serializeArray(val []any) (buf []byte, err error) {
|
|
|
|
var (
|
|
|
|
ok bool
|
|
|
|
vs [][]any
|
|
|
|
normalFormat bool
|
|
|
|
isArrayElement bool
|
|
|
|
isStructElement bool
|
|
|
|
columnName string
|
|
|
|
)
|
|
|
|
normalFormat = true
|
|
|
|
for _, row := range val {
|
|
|
|
kind := reflect.Indirect(reflect.ValueOf(row)).Kind()
|
|
|
|
if !isNormalKind(kind) {
|
|
|
|
normalFormat = false
|
|
|
|
}
|
|
|
|
if kind == reflect.Array || kind == reflect.Slice {
|
|
|
|
isArrayElement = true
|
|
|
|
}
|
|
|
|
if kind == reflect.Struct {
|
|
|
|
isStructElement = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if normalFormat {
|
|
|
|
goto __END
|
|
|
|
}
|
|
|
|
if isArrayElement {
|
|
|
|
vs = make([][]any, 0, len(val))
|
|
|
|
for _, v := range val {
|
|
|
|
rv := reflect.Indirect(reflect.ValueOf(v))
|
|
|
|
if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
|
|
|
|
row := make([]any, 0, rv.Len())
|
|
|
|
for i := 0; i < rv.Len(); i++ {
|
2024-01-18 17:11:44 +08:00
|
|
|
if isNormalKind(rv.Index(i).Kind()) || rv.Index(i).Interface() == nil {
|
2023-04-23 17:57:36 +08:00
|
|
|
row = append(row, rv.Index(i).Interface())
|
|
|
|
} else {
|
|
|
|
goto __END
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vs = append(vs, row)
|
|
|
|
} else {
|
|
|
|
goto __END
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if isStructElement {
|
|
|
|
vs = make([][]any, 0, len(val))
|
2024-04-29 15:17:23 +08:00
|
|
|
indexes := make([]int, 0)
|
2023-04-23 17:57:36 +08:00
|
|
|
for i, v := range val {
|
|
|
|
rv := reflect.Indirect(reflect.ValueOf(v))
|
|
|
|
if rv.Kind() == reflect.Struct {
|
|
|
|
if i == 0 {
|
|
|
|
row := make([]any, 0, rv.Type().NumField())
|
|
|
|
for j := 0; j < rv.Type().NumField(); j++ {
|
|
|
|
st := rv.Type().Field(j).Tag
|
2024-04-29 10:52:19 +08:00
|
|
|
if columnName, ok = st.Lookup("kos"); !ok {
|
2023-04-23 17:57:36 +08:00
|
|
|
columnName = strings.ToUpper(rv.Type().Field(j).Name)
|
2024-04-29 10:52:19 +08:00
|
|
|
} else {
|
|
|
|
if columnName == "-" {
|
|
|
|
continue
|
|
|
|
}
|
2023-04-23 17:57:36 +08:00
|
|
|
}
|
2024-11-12 17:36:44 +08:00
|
|
|
if !rv.Type().Field(j).IsExported() {
|
|
|
|
continue
|
|
|
|
}
|
2024-04-29 15:17:23 +08:00
|
|
|
indexes = append(indexes, j)
|
2023-04-23 17:57:36 +08:00
|
|
|
row = append(row, columnName)
|
|
|
|
}
|
|
|
|
vs = append(vs, row)
|
|
|
|
}
|
|
|
|
row := make([]any, 0, rv.Type().NumField())
|
|
|
|
for j := 0; j < rv.Type().NumField(); j++ {
|
2024-04-29 15:17:23 +08:00
|
|
|
if arrays.Exists(j, indexes) {
|
|
|
|
row = append(row, rv.Field(j).Interface())
|
|
|
|
}
|
2023-04-23 17:57:36 +08:00
|
|
|
}
|
|
|
|
vs = append(vs, row)
|
|
|
|
} else {
|
|
|
|
goto __END
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf = printArray(vs)
|
|
|
|
return
|
|
|
|
__END:
|
|
|
|
return json.MarshalIndent(val, "", "\t")
|
|
|
|
}
|
|
|
|
|
|
|
|
func serialize(val any) (buf []byte, err error) {
|
|
|
|
var (
|
|
|
|
refVal reflect.Value
|
|
|
|
)
|
|
|
|
refVal = reflect.Indirect(reflect.ValueOf(val))
|
|
|
|
switch refVal.Kind() {
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
|
buf = []byte(strconv.FormatInt(refVal.Int(), 10))
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
buf = []byte(strconv.FormatFloat(refVal.Float(), 'f', -1, 64))
|
|
|
|
case reflect.String:
|
|
|
|
buf = []byte(refVal.String())
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
if refVal.Type().Elem().Kind() == reflect.Uint8 {
|
|
|
|
buf = refVal.Bytes()
|
|
|
|
} else {
|
|
|
|
as := make([]any, 0, refVal.Len())
|
|
|
|
for i := 0; i < refVal.Len(); i++ {
|
|
|
|
as = append(as, refVal.Index(i).Interface())
|
|
|
|
}
|
|
|
|
buf, err = serializeArray(as)
|
|
|
|
}
|
|
|
|
case reflect.Map:
|
|
|
|
ms := make(map[any]any)
|
|
|
|
keys := refVal.MapKeys()
|
|
|
|
for _, key := range keys {
|
|
|
|
ms[key.Interface()] = refVal.MapIndex(key).Interface()
|
|
|
|
}
|
|
|
|
buf, err = serializeMap(ms)
|
|
|
|
default:
|
|
|
|
buf, err = json.MarshalIndent(refVal.Interface(), "", "\t")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|