123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- package cli
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "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++ {
- if isNormalKind(rv.Index(i).Kind()) || rv.Index(i).Interface() == nil {
- 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))
- 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
- if columnName, ok = st.Lookup("name"); !ok {
- columnName = strings.ToUpper(rv.Type().Field(j).Name)
- }
- row = append(row, columnName)
- }
- vs = append(vs, row)
- }
- row := make([]any, 0, rv.Type().NumField())
- for j := 0; j < rv.Type().NumField(); j++ {
- row = append(row, rv.Field(j).Interface())
- }
- 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
- }
|