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 }