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  }