package toml import ( "bytes" "encoding" "encoding/json" "fmt" "io" "io/ioutil" "math" "os" "reflect" "strconv" "strings" "time" ) // Unmarshaler is the interface implemented by objects that can unmarshal a // TOML description of themselves. type Unmarshaler interface { UnmarshalTOML(interface{}) error } // Unmarshal decodes the contents of data in TOML format into a pointer v. // // See [Decoder] for a description of the decoding process. func Unmarshal(data []byte, v interface{}) error { _, err := NewDecoder(bytes.NewReader(data)).Decode(v) return err } // Decode the TOML data in to the pointer v. // // See [Decoder] for a description of the decoding process. func Decode(data string, v interface{}) (MetaData, error) { return NewDecoder(strings.NewReader(data)).Decode(v) } // DecodeFile reads the contents of a file and decodes it with [Decode]. func DecodeFile(path string, v interface{}) (MetaData, error) { fp, err := os.Open(path) if err != nil { return MetaData{}, err } defer fp.Close() return NewDecoder(fp).Decode(v) } // Primitive is a TOML value that hasn't been decoded into a Go value. // // This type can be used for any value, which will cause decoding to be delayed. // You can use [PrimitiveDecode] to "manually" decode these values. // // NOTE: The underlying representation of a `Primitive` value is subject to // change. Do not rely on it. // // NOTE: Primitive values are still parsed, so using them will only avoid the // overhead of reflection. They can be useful when you don't know the exact type // of TOML data until runtime. type Primitive struct { undecoded interface{} context Key } // The significand precision for float32 and float64 is 24 and 53 bits; this is // the range a natural number can be stored in a float without loss of data. const ( maxSafeFloat32Int = 16777215 // 2^24-1 maxSafeFloat64Int = int64(9007199254740991) // 2^53-1 ) // Decoder decodes TOML data. // // TOML tables correspond to Go structs or maps; they can be used // interchangeably, but structs offer better type safety. // // TOML table arrays correspond to either a slice of structs or a slice of maps. // // TOML datetimes correspond to [time.Time]. Local datetimes are parsed in the // local timezone. // // [time.Duration] types are treated as nanoseconds if the TOML value is an // integer, or they're parsed with time.ParseDuration() if they're strings. // // All other TOML types (float, string, int, bool and array) correspond to the // obvious Go types. // // An exception to the above rules is if a type implements the TextUnmarshaler // interface, in which case any primitive TOML value (floats, strings, integers, // booleans, datetimes) will be converted to a []byte and given to the value's // UnmarshalText method. See the Unmarshaler example for a demonstration with // email addresses. // // # Key mapping // // TOML keys can map to either keys in a Go map or field names in a Go struct. // The special `toml` struct tag can be used to map TOML keys to struct fields // that don't match the key name exactly (see the example). A case insensitive // match to struct names will be tried if an exact match can't be found. // // The mapping between TOML values and Go values is loose. That is, there may // exist TOML values that cannot be placed into your representation, and there // may be parts of your representation that do not correspond to TOML values. // This loose mapping can be made stricter by using the IsDefined and/or // Undecoded methods on the MetaData returned. // // This decoder does not handle cyclic types. Decode will not terminate if a // cyclic type is passed. type Decoder struct { r io.Reader } // NewDecoder creates a new Decoder. func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } var ( unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem() unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() primitiveType = reflect.TypeOf((*Primitive)(nil)).Elem() ) // Decode TOML data in to the pointer `v`. func (dec *Decoder) Decode(v interface{}) (MetaData, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { s := "%q" if reflect.TypeOf(v) == nil { s = "%v" } return MetaData{}, fmt.Errorf("toml: cannot decode to non-pointer "+s, reflect.TypeOf(v)) } if rv.IsNil() { return MetaData{}, fmt.Errorf("toml: cannot decode to nil value of %q", reflect.TypeOf(v)) } // Check if this is a supported type: struct, map, interface{}, or something // that implements UnmarshalTOML or UnmarshalText. rv = indirect(rv) rt := rv.Type() if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map && !(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) && !rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) { return MetaData{}, fmt.Errorf("toml: cannot decode to type %s", rt) } // TODO: parser should read from io.Reader? Or at the very least, make it // read from []byte rather than string data, err := ioutil.ReadAll(dec.r) if err != nil { return MetaData{}, err } p, err := parse(string(data)) if err != nil { return MetaData{}, err } md := MetaData{ mapping: p.mapping, keyInfo: p.keyInfo, keys: p.ordered, decoded: make(map[string]struct{}, len(p.ordered)), context: nil, data: data, } return md, md.unify(p.mapping, rv) } // PrimitiveDecode is just like the other Decode* functions, except it decodes a // TOML value that has already been parsed. Valid primitive values can *only* be // obtained from values filled by the decoder functions, including this method. // (i.e., v may contain more [Primitive] values.) // // Meta data for primitive values is included in the meta data returned by the // Decode* functions with one exception: keys returned by the Undecoded method // will only reflect keys that were decoded. Namely, any keys hidden behind a // Primitive will be considered undecoded. Executing this method will update the // undecoded keys in the meta data. (See the example.) func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { md.context = primValue.context defer func() { md.context = nil }() return md.unify(primValue.undecoded, rvalue(v)) } // unify performs a sort of type unification based on the structure of `rv`, // which is the client representation. // // Any type mismatch produces an error. Finding a type that we don't know // how to handle produces an unsupported type error. func (md *MetaData) unify(data interface{}, rv reflect.Value) error { // Special case. Look for a `Primitive` value. // TODO: #76 would make this superfluous after implemented. if rv.Type() == primitiveType { // Save the undecoded data and the key context into the primitive // value. context := make(Key, len(md.context)) copy(context, md.context) rv.Set(reflect.ValueOf(Primitive{ undecoded: data, context: context, })) return nil } rvi := rv.Interface() if v, ok := rvi.(Unmarshaler); ok { return v.UnmarshalTOML(data) } if v, ok := rvi.(encoding.TextUnmarshaler); ok { return md.unifyText(data, v) } // TODO: // The behavior here is incorrect whenever a Go type satisfies the // encoding.TextUnmarshaler interface but also corresponds to a TOML hash or // array. In particular, the unmarshaler should only be applied to primitive // TOML values. But at this point, it will be applied to all kinds of values // and produce an incorrect error whenever those values are hashes or arrays // (including arrays of tables). k := rv.Kind() if k >= reflect.Int && k <= reflect.Uint64 { return md.unifyInt(data, rv) } switch k { case reflect.Ptr: elem := reflect.New(rv.Type().Elem()) err := md.unify(data, reflect.Indirect(elem)) if err != nil { return err } rv.Set(elem) return nil case reflect.Struct: return md.unifyStruct(data, rv) case reflect.Map: return md.unifyMap(data, rv) case reflect.Array: return md.unifyArray(data, rv) case reflect.Slice: return md.unifySlice(data, rv) case reflect.String: return md.unifyString(data, rv) case reflect.Bool: return md.unifyBool(data, rv) case reflect.Interface: if rv.NumMethod() > 0 { /// Only empty interfaces are supported. return md.e("unsupported type %s", rv.Type()) } return md.unifyAnything(data, rv) case reflect.Float32, reflect.Float64: return md.unifyFloat64(data, rv) } return md.e("unsupported type %s", rv.Kind()) } func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { tmap, ok := mapping.(map[string]interface{}) if !ok { if mapping == nil { return nil } return md.e("type mismatch for %s: expected table but found %T", rv.Type().String(), mapping) } for key, datum := range tmap { var f *field fields := cachedTypeFields(rv.Type()) for i := range fields { ff := &fields[i] if ff.name == key { f = ff break } if f == nil && strings.EqualFold(ff.name, key) { f = ff } } if f != nil { subv := rv for _, i := range f.index { subv = indirect(subv.Field(i)) } if isUnifiable(subv) { md.decoded[md.context.add(key).String()] = struct{}{} md.context = append(md.context, key) err := md.unify(datum, subv) if err != nil { return err } md.context = md.context[0 : len(md.context)-1] } else if f.name != "" { return md.e("cannot write unexported field %s.%s", rv.Type().String(), f.name) } } } return nil } func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { keyType := rv.Type().Key().Kind() if keyType != reflect.String && keyType != reflect.Interface { return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)", keyType, rv.Type()) } tmap, ok := mapping.(map[string]interface{}) if !ok { if tmap == nil { return nil } return md.badtype("map", mapping) } if rv.IsNil() { rv.Set(reflect.MakeMap(rv.Type())) } for k, v := range tmap { md.decoded[md.context.add(k).String()] = struct{}{} md.context = append(md.context, k) rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) err := md.unify(v, indirect(rvval)) if err != nil { return err } md.context = md.context[0 : len(md.context)-1] rvkey := indirect(reflect.New(rv.Type().Key())) switch keyType { case reflect.Interface: rvkey.Set(reflect.ValueOf(k)) case reflect.String: rvkey.SetString(k) } rv.SetMapIndex(rvkey, rvval) } return nil } func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { datav := reflect.ValueOf(data) if datav.Kind() != reflect.Slice { if !datav.IsValid() { return nil } return md.badtype("slice", data) } if l := datav.Len(); l != rv.Len() { return md.e("expected array length %d; got TOML array of length %d", rv.Len(), l) } return md.unifySliceArray(datav, rv) } func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { datav := reflect.ValueOf(data) if datav.Kind() != reflect.Slice { if !datav.IsValid() { return nil } return md.badtype("slice", data) } n := datav.Len() if rv.IsNil() || rv.Cap() < n { rv.Set(reflect.MakeSlice(rv.Type(), n, n)) } rv.SetLen(n) return md.unifySliceArray(datav, rv) } func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { l := data.Len() for i := 0; i < l; i++ { err := md.unify(data.Index(i).Interface(), indirect(rv.Index(i))) if err != nil { return err } } return nil } func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { _, ok := rv.Interface().(json.Number) if ok { if i, ok := data.(int64); ok { rv.SetString(strconv.FormatInt(i, 10)) } else if f, ok := data.(float64); ok { rv.SetString(strconv.FormatFloat(f, 'f', -1, 64)) } else { return md.badtype("string", data) } return nil } if s, ok := data.(string); ok { rv.SetString(s) return nil } return md.badtype("string", data) } func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { rvk := rv.Kind() if num, ok := data.(float64); ok { switch rvk { case reflect.Float32: if num < -math.MaxFloat32 || num > math.MaxFloat32 { return md.parseErr(errParseRange{i: num, size: rvk.String()}) } fallthrough case reflect.Float64: rv.SetFloat(num) default: panic("bug") } return nil } if num, ok := data.(int64); ok { if (rvk == reflect.Float32 && (num < -maxSafeFloat32Int || num > maxSafeFloat32Int)) || (rvk == reflect.Float64 && (num < -maxSafeFloat64Int || num > maxSafeFloat64Int)) { return md.parseErr(errParseRange{i: num, size: rvk.String()}) } rv.SetFloat(float64(num)) return nil } return md.badtype("float", data) } func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { _, ok := rv.Interface().(time.Duration) if ok { // Parse as string duration, and fall back to regular integer parsing // (as nanosecond) if this is not a string. if s, ok := data.(string); ok { dur, err := time.ParseDuration(s) if err != nil { return md.parseErr(errParseDuration{s}) } rv.SetInt(int64(dur)) return nil } } num, ok := data.(int64) if !ok { return md.badtype("integer", data) } rvk := rv.Kind() switch { case rvk >= reflect.Int && rvk <= reflect.Int64: if (rvk == reflect.Int8 && (num < math.MinInt8 || num > math.MaxInt8)) || (rvk == reflect.Int16 && (num < math.MinInt16 || num > math.MaxInt16)) || (rvk == reflect.Int32 && (num < math.MinInt32 || num > math.MaxInt32)) { return md.parseErr(errParseRange{i: num, size: rvk.String()}) } rv.SetInt(num) case rvk >= reflect.Uint && rvk <= reflect.Uint64: unum := uint64(num) if rvk == reflect.Uint8 && (num < 0 || unum > math.MaxUint8) || rvk == reflect.Uint16 && (num < 0 || unum > math.MaxUint16) || rvk == reflect.Uint32 && (num < 0 || unum > math.MaxUint32) { return md.parseErr(errParseRange{i: num, size: rvk.String()}) } rv.SetUint(unum) default: panic("unreachable") } return nil } func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { if b, ok := data.(bool); ok { rv.SetBool(b) return nil } return md.badtype("boolean", data) } func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { rv.Set(reflect.ValueOf(data)) return nil } func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error { var s string switch sdata := data.(type) { case Marshaler: text, err := sdata.MarshalTOML() if err != nil { return err } s = string(text) case encoding.TextMarshaler: text, err := sdata.MarshalText() if err != nil { return err } s = string(text) case fmt.Stringer: s = sdata.String() case string: s = sdata case bool: s = fmt.Sprintf("%v", sdata) case int64: s = fmt.Sprintf("%d", sdata) case float64: s = fmt.Sprintf("%f", sdata) default: return md.badtype("primitive (string-like)", data) } if err := v.UnmarshalText([]byte(s)); err != nil { return err } return nil } func (md *MetaData) badtype(dst string, data interface{}) error { return md.e("incompatible types: TOML value has type %T; destination has type %s", data, dst) } func (md *MetaData) parseErr(err error) error { k := md.context.String() return ParseError{ LastKey: k, Position: md.keyInfo[k].pos, Line: md.keyInfo[k].pos.Line, err: err, input: string(md.data), } } func (md *MetaData) e(format string, args ...interface{}) error { f := "toml: " if len(md.context) > 0 { f = fmt.Sprintf("toml: (last key %q): ", md.context) p := md.keyInfo[md.context.String()].pos if p.Line > 0 { f = fmt.Sprintf("toml: line %d (last key %q): ", p.Line, md.context) } } return fmt.Errorf(f+format, args...) } // rvalue returns a reflect.Value of `v`. All pointers are resolved. func rvalue(v interface{}) reflect.Value { return indirect(reflect.ValueOf(v)) } // indirect returns the value pointed to by a pointer. // // Pointers are followed until the value is not a pointer. New values are // allocated for each nil pointer. // // An exception to this rule is if the value satisfies an interface of interest // to us (like encoding.TextUnmarshaler). func indirect(v reflect.Value) reflect.Value { if v.Kind() != reflect.Ptr { if v.CanSet() { pv := v.Addr() pvi := pv.Interface() if _, ok := pvi.(encoding.TextUnmarshaler); ok { return pv } if _, ok := pvi.(Unmarshaler); ok { return pv } } return v } if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } return indirect(reflect.Indirect(v)) } func isUnifiable(rv reflect.Value) bool { if rv.CanSet() { return true } rvi := rv.Interface() if _, ok := rvi.(encoding.TextUnmarshaler); ok { return true } if _, ok := rvi.(Unmarshaler); ok { return true } return false }