...

Source file src/github.com/mitchellh/mapstructure/decode_hooks.go

Documentation: github.com/mitchellh/mapstructure

     1  package mapstructure
     2  
     3  import (
     4  	"encoding"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  )
    13  
    14  // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
    15  // it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
    16  func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
    17  	// Create variables here so we can reference them with the reflect pkg
    18  	var f1 DecodeHookFuncType
    19  	var f2 DecodeHookFuncKind
    20  	var f3 DecodeHookFuncValue
    21  
    22  	// Fill in the variables into this interface and the rest is done
    23  	// automatically using the reflect package.
    24  	potential := []interface{}{f1, f2, f3}
    25  
    26  	v := reflect.ValueOf(h)
    27  	vt := v.Type()
    28  	for _, raw := range potential {
    29  		pt := reflect.ValueOf(raw).Type()
    30  		if vt.ConvertibleTo(pt) {
    31  			return v.Convert(pt).Interface()
    32  		}
    33  	}
    34  
    35  	return nil
    36  }
    37  
    38  // DecodeHookExec executes the given decode hook. This should be used
    39  // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
    40  // that took reflect.Kind instead of reflect.Type.
    41  func DecodeHookExec(
    42  	raw DecodeHookFunc,
    43  	from reflect.Value, to reflect.Value) (interface{}, error) {
    44  
    45  	switch f := typedDecodeHook(raw).(type) {
    46  	case DecodeHookFuncType:
    47  		return f(from.Type(), to.Type(), from.Interface())
    48  	case DecodeHookFuncKind:
    49  		return f(from.Kind(), to.Kind(), from.Interface())
    50  	case DecodeHookFuncValue:
    51  		return f(from, to)
    52  	default:
    53  		return nil, errors.New("invalid decode hook signature")
    54  	}
    55  }
    56  
    57  // ComposeDecodeHookFunc creates a single DecodeHookFunc that
    58  // automatically composes multiple DecodeHookFuncs.
    59  //
    60  // The composed funcs are called in order, with the result of the
    61  // previous transformation.
    62  func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
    63  	return func(f reflect.Value, t reflect.Value) (interface{}, error) {
    64  		var err error
    65  		data := f.Interface()
    66  
    67  		newFrom := f
    68  		for _, f1 := range fs {
    69  			data, err = DecodeHookExec(f1, newFrom, t)
    70  			if err != nil {
    71  				return nil, err
    72  			}
    73  			newFrom = reflect.ValueOf(data)
    74  		}
    75  
    76  		return data, nil
    77  	}
    78  }
    79  
    80  // OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
    81  // If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
    82  func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
    83  	return func(a, b reflect.Value) (interface{}, error) {
    84  		var allErrs string
    85  		var out interface{}
    86  		var err error
    87  
    88  		for _, f := range ff {
    89  			out, err = DecodeHookExec(f, a, b)
    90  			if err != nil {
    91  				allErrs += err.Error() + "\n"
    92  				continue
    93  			}
    94  
    95  			return out, nil
    96  		}
    97  
    98  		return nil, errors.New(allErrs)
    99  	}
   100  }
   101  
   102  // StringToSliceHookFunc returns a DecodeHookFunc that converts
   103  // string to []string by splitting on the given sep.
   104  func StringToSliceHookFunc(sep string) DecodeHookFunc {
   105  	return func(
   106  		f reflect.Kind,
   107  		t reflect.Kind,
   108  		data interface{}) (interface{}, error) {
   109  		if f != reflect.String || t != reflect.Slice {
   110  			return data, nil
   111  		}
   112  
   113  		raw := data.(string)
   114  		if raw == "" {
   115  			return []string{}, nil
   116  		}
   117  
   118  		return strings.Split(raw, sep), nil
   119  	}
   120  }
   121  
   122  // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
   123  // strings to time.Duration.
   124  func StringToTimeDurationHookFunc() DecodeHookFunc {
   125  	return func(
   126  		f reflect.Type,
   127  		t reflect.Type,
   128  		data interface{}) (interface{}, error) {
   129  		if f.Kind() != reflect.String {
   130  			return data, nil
   131  		}
   132  		if t != reflect.TypeOf(time.Duration(5)) {
   133  			return data, nil
   134  		}
   135  
   136  		// Convert it by parsing
   137  		return time.ParseDuration(data.(string))
   138  	}
   139  }
   140  
   141  // StringToIPHookFunc returns a DecodeHookFunc that converts
   142  // strings to net.IP
   143  func StringToIPHookFunc() DecodeHookFunc {
   144  	return func(
   145  		f reflect.Type,
   146  		t reflect.Type,
   147  		data interface{}) (interface{}, error) {
   148  		if f.Kind() != reflect.String {
   149  			return data, nil
   150  		}
   151  		if t != reflect.TypeOf(net.IP{}) {
   152  			return data, nil
   153  		}
   154  
   155  		// Convert it by parsing
   156  		ip := net.ParseIP(data.(string))
   157  		if ip == nil {
   158  			return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
   159  		}
   160  
   161  		return ip, nil
   162  	}
   163  }
   164  
   165  // StringToIPNetHookFunc returns a DecodeHookFunc that converts
   166  // strings to net.IPNet
   167  func StringToIPNetHookFunc() DecodeHookFunc {
   168  	return func(
   169  		f reflect.Type,
   170  		t reflect.Type,
   171  		data interface{}) (interface{}, error) {
   172  		if f.Kind() != reflect.String {
   173  			return data, nil
   174  		}
   175  		if t != reflect.TypeOf(net.IPNet{}) {
   176  			return data, nil
   177  		}
   178  
   179  		// Convert it by parsing
   180  		_, net, err := net.ParseCIDR(data.(string))
   181  		return net, err
   182  	}
   183  }
   184  
   185  // StringToTimeHookFunc returns a DecodeHookFunc that converts
   186  // strings to time.Time.
   187  func StringToTimeHookFunc(layout string) DecodeHookFunc {
   188  	return func(
   189  		f reflect.Type,
   190  		t reflect.Type,
   191  		data interface{}) (interface{}, error) {
   192  		if f.Kind() != reflect.String {
   193  			return data, nil
   194  		}
   195  		if t != reflect.TypeOf(time.Time{}) {
   196  			return data, nil
   197  		}
   198  
   199  		// Convert it by parsing
   200  		return time.Parse(layout, data.(string))
   201  	}
   202  }
   203  
   204  // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
   205  // the decoder.
   206  //
   207  // Note that this is significantly different from the WeaklyTypedInput option
   208  // of the DecoderConfig.
   209  func WeaklyTypedHook(
   210  	f reflect.Kind,
   211  	t reflect.Kind,
   212  	data interface{}) (interface{}, error) {
   213  	dataVal := reflect.ValueOf(data)
   214  	switch t {
   215  	case reflect.String:
   216  		switch f {
   217  		case reflect.Bool:
   218  			if dataVal.Bool() {
   219  				return "1", nil
   220  			}
   221  			return "0", nil
   222  		case reflect.Float32:
   223  			return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
   224  		case reflect.Int:
   225  			return strconv.FormatInt(dataVal.Int(), 10), nil
   226  		case reflect.Slice:
   227  			dataType := dataVal.Type()
   228  			elemKind := dataType.Elem().Kind()
   229  			if elemKind == reflect.Uint8 {
   230  				return string(dataVal.Interface().([]uint8)), nil
   231  			}
   232  		case reflect.Uint:
   233  			return strconv.FormatUint(dataVal.Uint(), 10), nil
   234  		}
   235  	}
   236  
   237  	return data, nil
   238  }
   239  
   240  func RecursiveStructToMapHookFunc() DecodeHookFunc {
   241  	return func(f reflect.Value, t reflect.Value) (interface{}, error) {
   242  		if f.Kind() != reflect.Struct {
   243  			return f.Interface(), nil
   244  		}
   245  
   246  		var i interface{} = struct{}{}
   247  		if t.Type() != reflect.TypeOf(&i).Elem() {
   248  			return f.Interface(), nil
   249  		}
   250  
   251  		m := make(map[string]interface{})
   252  		t.Set(reflect.ValueOf(m))
   253  
   254  		return f.Interface(), nil
   255  	}
   256  }
   257  
   258  // TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
   259  // strings to the UnmarshalText function, when the target type
   260  // implements the encoding.TextUnmarshaler interface
   261  func TextUnmarshallerHookFunc() DecodeHookFuncType {
   262  	return func(
   263  		f reflect.Type,
   264  		t reflect.Type,
   265  		data interface{}) (interface{}, error) {
   266  		if f.Kind() != reflect.String {
   267  			return data, nil
   268  		}
   269  		result := reflect.New(t).Interface()
   270  		unmarshaller, ok := result.(encoding.TextUnmarshaler)
   271  		if !ok {
   272  			return data, nil
   273  		}
   274  		if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
   275  			return nil, err
   276  		}
   277  		return result, nil
   278  	}
   279  }
   280  

View as plain text