github.com/yandex/pandora@v0.5.32/core/config/config.go (about) 1 package config 2 3 import ( 4 "github.com/mitchellh/mapstructure" 5 "github.com/pkg/errors" 6 ) 7 8 const TagName = "config" 9 10 // Decodes conf to result. Doesn't zero fields. 11 func Decode(conf interface{}, result interface{}) error { 12 decoder, err := mapstructure.NewDecoder(newDecoderConfig(result)) 13 if err != nil { 14 return errors.WithStack(err) 15 } 16 return errors.WithStack(decoder.Decode(conf)) 17 } 18 19 func DecodeAndValidate(conf interface{}, result interface{}) error { 20 err := Decode(conf, result) 21 if err != nil { 22 return err 23 } 24 return Validate(result) 25 } 26 27 // Map maps with overwrite fields from src to dst. 28 // if src filed have `map:""` tag, tag value will 29 // be used as dst field destination. 30 // src field destinations should be subset of dst fields. 31 // dst should be struct pointer. src should be struct or struct pointer. 32 // Example: you need to configure only some subset fields of struct Multi, 33 // in such case you can from this subset of fields struct Single, decode config 34 // into it, and map it on Multi. 35 func Map(dst, src interface{}) { 36 // dst and src conf for compatibility with old fatih/structs. 37 // src map from "map:" tags -> tmp -> map to "mapstructure:" tags in dst 38 dstConf := &mapstructure.DecoderConfig{ 39 ErrorUnused: true, 40 ZeroFields: true, 41 Result: dst, 42 } 43 d, err := mapstructure.NewDecoder(dstConf) 44 if err != nil { 45 panic(err) 46 } 47 48 tmp := make(map[string]interface{}) 49 srcConf := &mapstructure.DecoderConfig{ 50 ErrorUnused: true, 51 ZeroFields: true, 52 Result: &tmp, 53 TagName: "map", 54 } 55 s, err := mapstructure.NewDecoder(srcConf) 56 if err != nil { 57 panic(err) 58 } 59 60 err = s.Decode(src) 61 if err != nil { 62 panic(err) 63 } 64 65 err = d.Decode(tmp) 66 if err != nil { 67 panic(err) 68 } 69 } 70 71 func newDecoderConfig(result interface{}) *mapstructure.DecoderConfig { 72 compileHooks() 73 return &mapstructure.DecoderConfig{ 74 DecodeHook: compiledHook, 75 ErrorUnused: true, 76 ZeroFields: false, 77 WeaklyTypedInput: false, 78 TagName: TagName, 79 Result: result, 80 } 81 } 82 83 type TypeHook mapstructure.DecodeHookFuncType 84 type KindHook mapstructure.DecodeHookFuncKind 85 86 // Returning value allow do `var _ = AddHookType(xxx)` 87 func AddTypeHook(hook TypeHook) (_ struct{}) { 88 addHook(hook) 89 return 90 } 91 92 func AddKindHook(hook KindHook) (_ struct{}) { 93 addHook(hook) 94 return 95 } 96 97 func DefaultHooks() []mapstructure.DecodeHookFunc { 98 return []mapstructure.DecodeHookFunc{ 99 VariableInjectHook, 100 DebugHook, 101 TextUnmarshallerHook, 102 mapstructure.StringToTimeDurationHookFunc(), 103 StringToURLHook, 104 StringToIPHook, 105 StringToDataSizeHook, 106 } 107 } 108 109 func GetHooks() []mapstructure.DecodeHookFunc { 110 return hooks 111 } 112 func SetHooks(h []mapstructure.DecodeHookFunc) { 113 hooks = h 114 onHooksModify() 115 } 116 117 var ( 118 hooks = DefaultHooks() 119 hooksNeedCompile = true 120 compiledHook mapstructure.DecodeHookFunc 121 ) 122 123 func addHook(hook mapstructure.DecodeHookFunc) { 124 hooks = append(hooks, hook) 125 onHooksModify() 126 } 127 128 func onHooksModify() { 129 hooksNeedCompile = true 130 } 131 132 func compileHooks() { 133 if hooksNeedCompile { 134 compiledHook = mapstructure.ComposeDecodeHookFunc(hooks...) 135 hooksNeedCompile = false 136 } 137 }