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