github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/cmd/hkserver/commands/utils.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"reflect"
     9  	"sort"
    10  	"strings"
    11  
    12  	"github.com/spf13/cobra"
    13  
    14  	"github.com/masterhung0112/hk_server/v5/model"
    15  	"github.com/masterhung0112/hk_server/v5/shared/mlog"
    16  )
    17  
    18  const CustomDefaultsEnvVar = "MM_CUSTOM_DEFAULTS_PATH"
    19  
    20  // prettyPrintStruct will return a prettyPrint version of a given struct
    21  func prettyPrintStruct(t interface{}) string {
    22  	return prettyPrintMap(structToMap(t))
    23  }
    24  
    25  // structToMap converts a struct into a map
    26  func structToMap(t interface{}) map[string]interface{} {
    27  	defer func() {
    28  		if r := recover(); r != nil {
    29  			mlog.Warn("Panicked in structToMap. This should never happen.", mlog.Any("recover", r))
    30  		}
    31  	}()
    32  
    33  	val := reflect.ValueOf(t)
    34  
    35  	if val.Kind() != reflect.Struct {
    36  		return nil
    37  	}
    38  
    39  	out := map[string]interface{}{}
    40  
    41  	for i := 0; i < val.NumField(); i++ {
    42  		field := val.Field(i)
    43  
    44  		var value interface{}
    45  
    46  		switch field.Kind() {
    47  		case reflect.Struct:
    48  			value = structToMap(field.Interface())
    49  		case reflect.Ptr:
    50  			indirectType := field.Elem()
    51  
    52  			if indirectType.Kind() == reflect.Struct {
    53  				value = structToMap(indirectType.Interface())
    54  			} else if indirectType.Kind() != reflect.Invalid {
    55  				value = indirectType.Interface()
    56  			}
    57  		default:
    58  			value = field.Interface()
    59  		}
    60  
    61  		out[val.Type().Field(i).Name] = value
    62  	}
    63  
    64  	return out
    65  }
    66  
    67  // prettyPrintMap will return a prettyPrint version of a given map
    68  func prettyPrintMap(configMap map[string]interface{}) string {
    69  	value := reflect.ValueOf(configMap)
    70  	return printStringMap(value, 0)
    71  }
    72  
    73  // printStringMap takes a reflect.Value and prints it out alphabetically based on key values, which must be strings.
    74  // This is done recursively if it's a map, and uses the given tab settings.
    75  func printStringMap(value reflect.Value, tabVal int) string {
    76  	out := &bytes.Buffer{}
    77  
    78  	var sortedKeys []string
    79  	stringToKeyMap := make(map[string]reflect.Value)
    80  	for _, k := range value.MapKeys() {
    81  		sortedKeys = append(sortedKeys, k.String())
    82  		stringToKeyMap[k.String()] = k
    83  	}
    84  
    85  	sort.Strings(sortedKeys)
    86  
    87  	for _, keyString := range sortedKeys {
    88  		key := stringToKeyMap[keyString]
    89  		val := value.MapIndex(key)
    90  		if newVal, ok := val.Interface().(map[string]interface{}); !ok {
    91  			fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal))
    92  			fmt.Fprintf(out, "%v: \"%v\"\n", key.Interface(), val.Interface())
    93  		} else {
    94  			fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal))
    95  			fmt.Fprintf(out, "%v:\n", key.Interface())
    96  			// going one level in, increase the tab
    97  			tabVal++
    98  			fmt.Fprintf(out, "%s", printStringMap(reflect.ValueOf(newVal), tabVal))
    99  			// coming back one level, decrease the tab
   100  			tabVal--
   101  		}
   102  	}
   103  
   104  	return out.String()
   105  }
   106  
   107  func getConfigDSN(command *cobra.Command, env map[string]string) string {
   108  	configDSN, _ := command.Flags().GetString("config")
   109  
   110  	// Config not supplied in flag, check env
   111  	if configDSN == "" {
   112  		configDSN = env["MM_CONFIG"]
   113  	}
   114  
   115  	// Config not supplied in env or flag use default
   116  	if configDSN == "" {
   117  		configDSN = "config.json"
   118  	}
   119  
   120  	return configDSN
   121  }
   122  
   123  func loadCustomDefaults() (*model.Config, error) {
   124  	customDefaultsPath := os.Getenv(CustomDefaultsEnvVar)
   125  	if customDefaultsPath == "" {
   126  		return nil, nil
   127  	}
   128  
   129  	file, err := os.Open(customDefaultsPath)
   130  	if err != nil {
   131  		return nil, fmt.Errorf("unable to open custom defaults file at %q: %w", customDefaultsPath, err)
   132  	}
   133  	defer file.Close()
   134  
   135  	var customDefaults *model.Config
   136  	err = json.NewDecoder(file).Decode(&customDefaults)
   137  	if err != nil {
   138  		return nil, fmt.Errorf("unable to decode custom defaults configuration: %w", err)
   139  	}
   140  
   141  	return customDefaults, nil
   142  }