github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/cmd/mattermost/commands/utils.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package commands 5 6 import ( 7 "bytes" 8 "fmt" 9 "reflect" 10 "sort" 11 "strings" 12 13 "github.com/mattermost/mattermost-server/v5/mlog" 14 ) 15 16 // prettyPrintStruct will return a prettyPrint version of a given struct 17 func prettyPrintStruct(t interface{}) string { 18 return prettyPrintMap(structToMap(t)) 19 } 20 21 // structToMap converts a struct into a map 22 func structToMap(t interface{}) map[string]interface{} { 23 defer func() { 24 if r := recover(); r != nil { 25 mlog.Error("Panicked in structToMap. This should never happen.", mlog.Any("recover", r)) 26 } 27 }() 28 29 val := reflect.ValueOf(t) 30 31 if val.Kind() != reflect.Struct { 32 return nil 33 } 34 35 out := map[string]interface{}{} 36 37 for i := 0; i < val.NumField(); i++ { 38 field := val.Field(i) 39 40 var value interface{} 41 42 switch field.Kind() { 43 case reflect.Struct: 44 value = structToMap(field.Interface()) 45 case reflect.Ptr: 46 indirectType := field.Elem() 47 48 if indirectType.Kind() == reflect.Struct { 49 value = structToMap(indirectType.Interface()) 50 } else if indirectType.Kind() != reflect.Invalid { 51 value = indirectType.Interface() 52 } 53 default: 54 value = field.Interface() 55 } 56 57 out[val.Type().Field(i).Name] = value 58 } 59 60 return out 61 } 62 63 // prettyPrintMap will return a prettyPrint version of a given map 64 func prettyPrintMap(configMap map[string]interface{}) string { 65 value := reflect.ValueOf(configMap) 66 return printStringMap(value, 0) 67 } 68 69 // printStringMap takes a reflect.Value and prints it out alphabetically based on key values, which must be strings. 70 // This is done recursively if it's a map, and uses the given tab settings. 71 func printStringMap(value reflect.Value, tabVal int) string { 72 out := &bytes.Buffer{} 73 74 var sortedKeys []string 75 stringToKeyMap := make(map[string]reflect.Value) 76 for _, k := range value.MapKeys() { 77 sortedKeys = append(sortedKeys, k.String()) 78 stringToKeyMap[k.String()] = k 79 } 80 81 sort.Strings(sortedKeys) 82 83 for _, keyString := range sortedKeys { 84 key := stringToKeyMap[keyString] 85 val := value.MapIndex(key) 86 if newVal, ok := val.Interface().(map[string]interface{}); !ok { 87 fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal)) 88 fmt.Fprintf(out, "%v: \"%v\"\n", key.Interface(), val.Interface()) 89 } else { 90 fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal)) 91 fmt.Fprintf(out, "%v:\n", key.Interface()) 92 // going one level in, increase the tab 93 tabVal++ 94 fmt.Fprintf(out, "%s", printStringMap(reflect.ValueOf(newVal), tabVal)) 95 // coming back one level, decrease the tab 96 tabVal-- 97 } 98 } 99 100 return out.String() 101 }