git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/toml/decode.go (about)

     1  package toml
     2  
     3  import (
     4  	"bytes"
     5  	"encoding"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"math"
    11  	"os"
    12  	"reflect"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  )
    17  
    18  // Unmarshaler is the interface implemented by objects that can unmarshal a
    19  // TOML description of themselves.
    20  type Unmarshaler interface {
    21  	UnmarshalTOML(interface{}) error
    22  }
    23  
    24  // Unmarshal decodes the contents of `data` in TOML format into a pointer `v`.
    25  func Unmarshal(data []byte, v interface{}) error {
    26  	_, err := NewDecoder(bytes.NewReader(data)).Decode(v)
    27  	return err
    28  }
    29  
    30  // Decode the TOML data in to the pointer v.
    31  //
    32  // See the documentation on Decoder for a description of the decoding process.
    33  func Decode(data string, v interface{}) (MetaData, error) {
    34  	return NewDecoder(strings.NewReader(data)).Decode(v)
    35  }
    36  
    37  // DecodeFile is just like Decode, except it will automatically read the
    38  // contents of the file at path and decode it for you.
    39  func DecodeFile(path string, v interface{}) (MetaData, error) {
    40  	fp, err := os.Open(path)
    41  	if err != nil {
    42  		return MetaData{}, err
    43  	}
    44  	defer fp.Close()
    45  	return NewDecoder(fp).Decode(v)
    46  }
    47  
    48  // Primitive is a TOML value that hasn't been decoded into a Go value.
    49  //
    50  // This type can be used for any value, which will cause decoding to be delayed.
    51  // You can use the PrimitiveDecode() function to "manually" decode these values.
    52  //
    53  // NOTE: The underlying representation of a `Primitive` value is subject to
    54  // change. Do not rely on it.
    55  //
    56  // NOTE: Primitive values are still parsed, so using them will only avoid the
    57  // overhead of reflection. They can be useful when you don't know the exact type
    58  // of TOML data until runtime.
    59  type Primitive struct {
    60  	undecoded interface{}
    61  	context   Key
    62  }
    63  
    64  // The significand precision for float32 and float64 is 24 and 53 bits; this is
    65  // the range a natural number can be stored in a float without loss of data.
    66  const (
    67  	maxSafeFloat32Int = 16777215                // 2^24-1
    68  	maxSafeFloat64Int = int64(9007199254740991) // 2^53-1
    69  )
    70  
    71  // Decoder decodes TOML data.
    72  //
    73  // TOML tables correspond to Go structs or maps (dealer's choice – they can be
    74  // used interchangeably).
    75  //
    76  // TOML table arrays correspond to either a slice of structs or a slice of maps.
    77  //
    78  // TOML datetimes correspond to Go time.Time values. Local datetimes are parsed
    79  // in the local timezone.
    80  //
    81  // time.Duration types are treated as nanoseconds if the TOML value is an
    82  // integer, or they're parsed with time.ParseDuration() if they're strings.
    83  //
    84  // All other TOML types (float, string, int, bool and array) correspond to the
    85  // obvious Go types.
    86  //
    87  // An exception to the above rules is if a type implements the TextUnmarshaler
    88  // interface, in which case any primitive TOML value (floats, strings, integers,
    89  // booleans, datetimes) will be converted to a []byte and given to the value's
    90  // UnmarshalText method. See the Unmarshaler example for a demonstration with
    91  // email addresses.
    92  //
    93  // # Key mapping
    94  //
    95  // TOML keys can map to either keys in a Go map or field names in a Go struct.
    96  // The special `toml` struct tag can be used to map TOML keys to struct fields
    97  // that don't match the key name exactly (see the example). A case insensitive
    98  // match to struct names will be tried if an exact match can't be found.
    99  //
   100  // The mapping between TOML values and Go values is loose. That is, there may
   101  // exist TOML values that cannot be placed into your representation, and there
   102  // may be parts of your representation that do not correspond to TOML values.
   103  // This loose mapping can be made stricter by using the IsDefined and/or
   104  // Undecoded methods on the MetaData returned.
   105  //
   106  // This decoder does not handle cyclic types. Decode will not terminate if a
   107  // cyclic type is passed.
   108  type Decoder struct {
   109  	r io.Reader
   110  }
   111  
   112  // NewDecoder creates a new Decoder.
   113  func NewDecoder(r io.Reader) *Decoder {
   114  	return &Decoder{r: r}
   115  }
   116  
   117  var (
   118  	unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
   119  	unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
   120  	primitiveType = reflect.TypeOf((*Primitive)(nil)).Elem()
   121  )
   122  
   123  // Decode TOML data in to the pointer `v`.
   124  func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
   125  	rv := reflect.ValueOf(v)
   126  	if rv.Kind() != reflect.Ptr {
   127  		s := "%q"
   128  		if reflect.TypeOf(v) == nil {
   129  			s = "%v"
   130  		}
   131  
   132  		return MetaData{}, fmt.Errorf("toml: cannot decode to non-pointer "+s, reflect.TypeOf(v))
   133  	}
   134  	if rv.IsNil() {
   135  		return MetaData{}, fmt.Errorf("toml: cannot decode to nil value of %q", reflect.TypeOf(v))
   136  	}
   137  
   138  	// Check if this is a supported type: struct, map, interface{}, or something
   139  	// that implements UnmarshalTOML or UnmarshalText.
   140  	rv = indirect(rv)
   141  	rt := rv.Type()
   142  	if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map &&
   143  		!(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) &&
   144  		!rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) {
   145  		return MetaData{}, fmt.Errorf("toml: cannot decode to type %s", rt)
   146  	}
   147  
   148  	// TODO: parser should read from io.Reader? Or at the very least, make it
   149  	// read from []byte rather than string
   150  	data, err := ioutil.ReadAll(dec.r)
   151  	if err != nil {
   152  		return MetaData{}, err
   153  	}
   154  
   155  	p, err := parse(string(data))
   156  	if err != nil {
   157  		return MetaData{}, err
   158  	}
   159  
   160  	md := MetaData{
   161  		mapping: p.mapping,
   162  		keyInfo: p.keyInfo,
   163  		keys:    p.ordered,
   164  		decoded: make(map[string]struct{}, len(p.ordered)),
   165  		context: nil,
   166  		data:    data,
   167  	}
   168  	return md, md.unify(p.mapping, rv)
   169  }
   170  
   171  // PrimitiveDecode is just like the other `Decode*` functions, except it
   172  // decodes a TOML value that has already been parsed. Valid primitive values
   173  // can *only* be obtained from values filled by the decoder functions,
   174  // including this method. (i.e., `v` may contain more `Primitive`
   175  // values.)
   176  //
   177  // Meta data for primitive values is included in the meta data returned by
   178  // the `Decode*` functions with one exception: keys returned by the Undecoded
   179  // method will only reflect keys that were decoded. Namely, any keys hidden
   180  // behind a Primitive will be considered undecoded. Executing this method will
   181  // update the undecoded keys in the meta data. (See the example.)
   182  func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
   183  	md.context = primValue.context
   184  	defer func() { md.context = nil }()
   185  	return md.unify(primValue.undecoded, rvalue(v))
   186  }
   187  
   188  // unify performs a sort of type unification based on the structure of `rv`,
   189  // which is the client representation.
   190  //
   191  // Any type mismatch produces an error. Finding a type that we don't know
   192  // how to handle produces an unsupported type error.
   193  func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
   194  	// Special case. Look for a `Primitive` value.
   195  	// TODO: #76 would make this superfluous after implemented.
   196  	if rv.Type() == primitiveType {
   197  		// Save the undecoded data and the key context into the primitive
   198  		// value.
   199  		context := make(Key, len(md.context))
   200  		copy(context, md.context)
   201  		rv.Set(reflect.ValueOf(Primitive{
   202  			undecoded: data,
   203  			context:   context,
   204  		}))
   205  		return nil
   206  	}
   207  
   208  	rvi := rv.Interface()
   209  	if v, ok := rvi.(Unmarshaler); ok {
   210  		return v.UnmarshalTOML(data)
   211  	}
   212  	if v, ok := rvi.(encoding.TextUnmarshaler); ok {
   213  		return md.unifyText(data, v)
   214  	}
   215  
   216  	// TODO:
   217  	// The behavior here is incorrect whenever a Go type satisfies the
   218  	// encoding.TextUnmarshaler interface but also corresponds to a TOML hash or
   219  	// array. In particular, the unmarshaler should only be applied to primitive
   220  	// TOML values. But at this point, it will be applied to all kinds of values
   221  	// and produce an incorrect error whenever those values are hashes or arrays
   222  	// (including arrays of tables).
   223  
   224  	k := rv.Kind()
   225  
   226  	if k >= reflect.Int && k <= reflect.Uint64 {
   227  		return md.unifyInt(data, rv)
   228  	}
   229  	switch k {
   230  	case reflect.Ptr:
   231  		elem := reflect.New(rv.Type().Elem())
   232  		err := md.unify(data, reflect.Indirect(elem))
   233  		if err != nil {
   234  			return err
   235  		}
   236  		rv.Set(elem)
   237  		return nil
   238  	case reflect.Struct:
   239  		return md.unifyStruct(data, rv)
   240  	case reflect.Map:
   241  		return md.unifyMap(data, rv)
   242  	case reflect.Array:
   243  		return md.unifyArray(data, rv)
   244  	case reflect.Slice:
   245  		return md.unifySlice(data, rv)
   246  	case reflect.String:
   247  		return md.unifyString(data, rv)
   248  	case reflect.Bool:
   249  		return md.unifyBool(data, rv)
   250  	case reflect.Interface:
   251  		if rv.NumMethod() > 0 { // Only support empty interfaces are supported.
   252  			return md.e("unsupported type %s", rv.Type())
   253  		}
   254  		return md.unifyAnything(data, rv)
   255  	case reflect.Float32, reflect.Float64:
   256  		return md.unifyFloat64(data, rv)
   257  	}
   258  	return md.e("unsupported type %s", rv.Kind())
   259  }
   260  
   261  func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
   262  	tmap, ok := mapping.(map[string]interface{})
   263  	if !ok {
   264  		if mapping == nil {
   265  			return nil
   266  		}
   267  		return md.e("type mismatch for %s: expected table but found %T",
   268  			rv.Type().String(), mapping)
   269  	}
   270  
   271  	for key, datum := range tmap {
   272  		var f *field
   273  		fields := cachedTypeFields(rv.Type())
   274  		for i := range fields {
   275  			ff := &fields[i]
   276  			if ff.name == key {
   277  				f = ff
   278  				break
   279  			}
   280  			if f == nil && strings.EqualFold(ff.name, key) {
   281  				f = ff
   282  			}
   283  		}
   284  		if f != nil {
   285  			subv := rv
   286  			for _, i := range f.index {
   287  				subv = indirect(subv.Field(i))
   288  			}
   289  
   290  			if isUnifiable(subv) {
   291  				md.decoded[md.context.add(key).String()] = struct{}{}
   292  				md.context = append(md.context, key)
   293  
   294  				err := md.unify(datum, subv)
   295  				if err != nil {
   296  					return err
   297  				}
   298  				md.context = md.context[0 : len(md.context)-1]
   299  			} else if f.name != "" {
   300  				return md.e("cannot write unexported field %s.%s", rv.Type().String(), f.name)
   301  			}
   302  		}
   303  	}
   304  	return nil
   305  }
   306  
   307  func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
   308  	keyType := rv.Type().Key().Kind()
   309  	if keyType != reflect.String && keyType != reflect.Interface {
   310  		return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)",
   311  			keyType, rv.Type())
   312  	}
   313  
   314  	tmap, ok := mapping.(map[string]interface{})
   315  	if !ok {
   316  		if tmap == nil {
   317  			return nil
   318  		}
   319  		return md.badtype("map", mapping)
   320  	}
   321  	if rv.IsNil() {
   322  		rv.Set(reflect.MakeMap(rv.Type()))
   323  	}
   324  	for k, v := range tmap {
   325  		md.decoded[md.context.add(k).String()] = struct{}{}
   326  		md.context = append(md.context, k)
   327  
   328  		rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
   329  
   330  		err := md.unify(v, indirect(rvval))
   331  		if err != nil {
   332  			return err
   333  		}
   334  		md.context = md.context[0 : len(md.context)-1]
   335  
   336  		rvkey := indirect(reflect.New(rv.Type().Key()))
   337  
   338  		switch keyType {
   339  		case reflect.Interface:
   340  			rvkey.Set(reflect.ValueOf(k))
   341  		case reflect.String:
   342  			rvkey.SetString(k)
   343  		}
   344  
   345  		rv.SetMapIndex(rvkey, rvval)
   346  	}
   347  	return nil
   348  }
   349  
   350  func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
   351  	datav := reflect.ValueOf(data)
   352  	if datav.Kind() != reflect.Slice {
   353  		if !datav.IsValid() {
   354  			return nil
   355  		}
   356  		return md.badtype("slice", data)
   357  	}
   358  	if l := datav.Len(); l != rv.Len() {
   359  		return md.e("expected array length %d; got TOML array of length %d", rv.Len(), l)
   360  	}
   361  	return md.unifySliceArray(datav, rv)
   362  }
   363  
   364  func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
   365  	datav := reflect.ValueOf(data)
   366  	if datav.Kind() != reflect.Slice {
   367  		if !datav.IsValid() {
   368  			return nil
   369  		}
   370  		return md.badtype("slice", data)
   371  	}
   372  	n := datav.Len()
   373  	if rv.IsNil() || rv.Cap() < n {
   374  		rv.Set(reflect.MakeSlice(rv.Type(), n, n))
   375  	}
   376  	rv.SetLen(n)
   377  	return md.unifySliceArray(datav, rv)
   378  }
   379  
   380  func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
   381  	l := data.Len()
   382  	for i := 0; i < l; i++ {
   383  		err := md.unify(data.Index(i).Interface(), indirect(rv.Index(i)))
   384  		if err != nil {
   385  			return err
   386  		}
   387  	}
   388  	return nil
   389  }
   390  
   391  func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
   392  	_, ok := rv.Interface().(json.Number)
   393  	if ok {
   394  		if i, ok := data.(int64); ok {
   395  			rv.SetString(strconv.FormatInt(i, 10))
   396  		} else if f, ok := data.(float64); ok {
   397  			rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
   398  		} else {
   399  			return md.badtype("string", data)
   400  		}
   401  		return nil
   402  	}
   403  
   404  	if s, ok := data.(string); ok {
   405  		rv.SetString(s)
   406  		return nil
   407  	}
   408  	return md.badtype("string", data)
   409  }
   410  
   411  func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
   412  	rvk := rv.Kind()
   413  
   414  	if num, ok := data.(float64); ok {
   415  		switch rvk {
   416  		case reflect.Float32:
   417  			if num < -math.MaxFloat32 || num > math.MaxFloat32 {
   418  				return md.parseErr(errParseRange{i: num, size: rvk.String()})
   419  			}
   420  			fallthrough
   421  		case reflect.Float64:
   422  			rv.SetFloat(num)
   423  		default:
   424  			panic("bug")
   425  		}
   426  		return nil
   427  	}
   428  
   429  	if num, ok := data.(int64); ok {
   430  		if (rvk == reflect.Float32 && (num < -maxSafeFloat32Int || num > maxSafeFloat32Int)) ||
   431  			(rvk == reflect.Float64 && (num < -maxSafeFloat64Int || num > maxSafeFloat64Int)) {
   432  			return md.parseErr(errParseRange{i: num, size: rvk.String()})
   433  		}
   434  		rv.SetFloat(float64(num))
   435  		return nil
   436  	}
   437  
   438  	return md.badtype("float", data)
   439  }
   440  
   441  func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
   442  	_, ok := rv.Interface().(time.Duration)
   443  	if ok {
   444  		// Parse as string duration, and fall back to regular integer parsing
   445  		// (as nanosecond) if this is not a string.
   446  		if s, ok := data.(string); ok {
   447  			dur, err := time.ParseDuration(s)
   448  			if err != nil {
   449  				return md.parseErr(errParseDuration{s})
   450  			}
   451  			rv.SetInt(int64(dur))
   452  			return nil
   453  		}
   454  	}
   455  
   456  	num, ok := data.(int64)
   457  	if !ok {
   458  		return md.badtype("integer", data)
   459  	}
   460  
   461  	rvk := rv.Kind()
   462  	switch {
   463  	case rvk >= reflect.Int && rvk <= reflect.Int64:
   464  		if (rvk == reflect.Int8 && (num < math.MinInt8 || num > math.MaxInt8)) ||
   465  			(rvk == reflect.Int16 && (num < math.MinInt16 || num > math.MaxInt16)) ||
   466  			(rvk == reflect.Int32 && (num < math.MinInt32 || num > math.MaxInt32)) {
   467  			return md.parseErr(errParseRange{i: num, size: rvk.String()})
   468  		}
   469  		rv.SetInt(num)
   470  	case rvk >= reflect.Uint && rvk <= reflect.Uint64:
   471  		unum := uint64(num)
   472  		if rvk == reflect.Uint8 && (num < 0 || unum > math.MaxUint8) ||
   473  			rvk == reflect.Uint16 && (num < 0 || unum > math.MaxUint16) ||
   474  			rvk == reflect.Uint32 && (num < 0 || unum > math.MaxUint32) {
   475  			return md.parseErr(errParseRange{i: num, size: rvk.String()})
   476  		}
   477  		rv.SetUint(unum)
   478  	default:
   479  		panic("unreachable")
   480  	}
   481  	return nil
   482  }
   483  
   484  func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
   485  	if b, ok := data.(bool); ok {
   486  		rv.SetBool(b)
   487  		return nil
   488  	}
   489  	return md.badtype("boolean", data)
   490  }
   491  
   492  func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
   493  	rv.Set(reflect.ValueOf(data))
   494  	return nil
   495  }
   496  
   497  func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error {
   498  	var s string
   499  	switch sdata := data.(type) {
   500  	case Marshaler:
   501  		text, err := sdata.MarshalTOML()
   502  		if err != nil {
   503  			return err
   504  		}
   505  		s = string(text)
   506  	case encoding.TextMarshaler:
   507  		text, err := sdata.MarshalText()
   508  		if err != nil {
   509  			return err
   510  		}
   511  		s = string(text)
   512  	case fmt.Stringer:
   513  		s = sdata.String()
   514  	case string:
   515  		s = sdata
   516  	case bool:
   517  		s = fmt.Sprintf("%v", sdata)
   518  	case int64:
   519  		s = fmt.Sprintf("%d", sdata)
   520  	case float64:
   521  		s = fmt.Sprintf("%f", sdata)
   522  	default:
   523  		return md.badtype("primitive (string-like)", data)
   524  	}
   525  	if err := v.UnmarshalText([]byte(s)); err != nil {
   526  		return err
   527  	}
   528  	return nil
   529  }
   530  
   531  func (md *MetaData) badtype(dst string, data interface{}) error {
   532  	return md.e("incompatible types: TOML value has type %T; destination has type %s", data, dst)
   533  }
   534  
   535  func (md *MetaData) parseErr(err error) error {
   536  	k := md.context.String()
   537  	return ParseError{
   538  		LastKey:  k,
   539  		Position: md.keyInfo[k].pos,
   540  		Line:     md.keyInfo[k].pos.Line,
   541  		err:      err,
   542  		input:    string(md.data),
   543  	}
   544  }
   545  
   546  func (md *MetaData) e(format string, args ...interface{}) error {
   547  	f := "toml: "
   548  	if len(md.context) > 0 {
   549  		f = fmt.Sprintf("toml: (last key %q): ", md.context)
   550  		p := md.keyInfo[md.context.String()].pos
   551  		if p.Line > 0 {
   552  			f = fmt.Sprintf("toml: line %d (last key %q): ", p.Line, md.context)
   553  		}
   554  	}
   555  	return fmt.Errorf(f+format, args...)
   556  }
   557  
   558  // rvalue returns a reflect.Value of `v`. All pointers are resolved.
   559  func rvalue(v interface{}) reflect.Value {
   560  	return indirect(reflect.ValueOf(v))
   561  }
   562  
   563  // indirect returns the value pointed to by a pointer.
   564  //
   565  // Pointers are followed until the value is not a pointer. New values are
   566  // allocated for each nil pointer.
   567  //
   568  // An exception to this rule is if the value satisfies an interface of interest
   569  // to us (like encoding.TextUnmarshaler).
   570  func indirect(v reflect.Value) reflect.Value {
   571  	if v.Kind() != reflect.Ptr {
   572  		if v.CanSet() {
   573  			pv := v.Addr()
   574  			pvi := pv.Interface()
   575  			if _, ok := pvi.(encoding.TextUnmarshaler); ok {
   576  				return pv
   577  			}
   578  			if _, ok := pvi.(Unmarshaler); ok {
   579  				return pv
   580  			}
   581  		}
   582  		return v
   583  	}
   584  	if v.IsNil() {
   585  		v.Set(reflect.New(v.Type().Elem()))
   586  	}
   587  	return indirect(reflect.Indirect(v))
   588  }
   589  
   590  func isUnifiable(rv reflect.Value) bool {
   591  	if rv.CanSet() {
   592  		return true
   593  	}
   594  	rvi := rv.Interface()
   595  	if _, ok := rvi.(encoding.TextUnmarshaler); ok {
   596  		return true
   597  	}
   598  	if _, ok := rvi.(Unmarshaler); ok {
   599  		return true
   600  	}
   601  	return false
   602  }