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  }