123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- // Package toml encodes and decodes the TOML configuration format using reflection.
- //
- // This library is compatible with TOML version v0.4.0.
- package toml
- import (
- "encoding"
- "fmt"
- "io"
- "io/ioutil"
- "reflect"
- "strconv"
- "strings"
- "time"
- "github.com/naoina/toml/ast"
- )
- const (
- tableSeparator = '.'
- )
- var (
- escapeReplacer = strings.NewReplacer(
- "\b", "\\n",
- "\f", "\\f",
- "\n", "\\n",
- "\r", "\\r",
- "\t", "\\t",
- )
- underscoreReplacer = strings.NewReplacer(
- "_", "",
- )
- )
- var timeType = reflect.TypeOf(time.Time{})
- // Unmarshal parses the TOML data and stores the result in the value pointed to by v.
- //
- // Unmarshal will mapped to v that according to following rules:
- //
- // TOML strings to string
- // TOML integers to any int type
- // TOML floats to float32 or float64
- // TOML booleans to bool
- // TOML datetimes to time.Time
- // TOML arrays to any type of slice
- // TOML tables to struct or map
- // TOML array tables to slice of struct or map
- func (cfg *Config) Unmarshal(data []byte, v interface{}) error {
- table, err := Parse(data)
- if err != nil {
- return err
- }
- if err := cfg.UnmarshalTable(table, v); err != nil {
- return err
- }
- return nil
- }
- // A Decoder reads and decodes TOML from an input stream.
- type Decoder struct {
- r io.Reader
- cfg *Config
- }
- // NewDecoder returns a new Decoder that reads from r.
- // Note that it reads all from r before parsing it.
- func (cfg *Config) NewDecoder(r io.Reader) *Decoder {
- return &Decoder{r, cfg}
- }
- // Decode parses the TOML data from its input and stores it in the value pointed to by v.
- // See the documentation for Unmarshal for details about the conversion of TOML into a Go value.
- func (d *Decoder) Decode(v interface{}) error {
- b, err := ioutil.ReadAll(d.r)
- if err != nil {
- return err
- }
- return d.cfg.Unmarshal(b, v)
- }
- // UnmarshalerRec may be implemented by types to customize their behavior when being
- // unmarshaled from TOML. You can use it to implement custom validation or to set
- // unexported fields.
- //
- // UnmarshalTOML receives a function that can be called to unmarshal the original TOML
- // value into a field or variable. It is safe to call the function more than once if
- // necessary.
- type UnmarshalerRec interface {
- UnmarshalTOML(fn func(interface{}) error) error
- }
- // Unmarshaler can be used to capture and process raw TOML source of a table or value.
- // UnmarshalTOML must copy the input if it wishes to retain it after returning.
- //
- // Note: this interface is retained for backwards compatibility. You probably want
- // to implement encoding.TextUnmarshaler or UnmarshalerRec instead.
- type Unmarshaler interface {
- UnmarshalTOML(input []byte) error
- }
- // UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
- //
- // UnmarshalTable will mapped to v that according to following rules:
- //
- // TOML strings to string
- // TOML integers to any int type
- // TOML floats to float32 or float64
- // TOML booleans to bool
- // TOML datetimes to time.Time
- // TOML arrays to any type of slice
- // TOML tables to struct or map
- // TOML array tables to slice of struct or map
- func (cfg *Config) UnmarshalTable(t *ast.Table, v interface{}) error {
- rv := reflect.ValueOf(v)
- toplevelMap := rv.Kind() == reflect.Map
- if (!toplevelMap && rv.Kind() != reflect.Ptr) || rv.IsNil() {
- return &invalidUnmarshalError{reflect.TypeOf(v)}
- }
- return unmarshalTable(cfg, rv, t, toplevelMap)
- }
- // used for UnmarshalerRec.
- func unmarshalTableOrValue(cfg *Config, rv reflect.Value, av interface{}) error {
- if (rv.Kind() != reflect.Ptr && rv.Kind() != reflect.Map) || rv.IsNil() {
- return &invalidUnmarshalError{rv.Type()}
- }
- rv = indirect(rv)
- switch av.(type) {
- case *ast.KeyValue, *ast.Table, []*ast.Table:
- if err := unmarshalField(cfg, rv, av); err != nil {
- return lineError(fieldLineNumber(av), err)
- }
- return nil
- case ast.Value:
- return setValue(cfg, rv, av.(ast.Value))
- default:
- panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
- }
- }
- // unmarshalTable unmarshals the fields of a table into a struct or map.
- //
- // toplevelMap is true when rv is an (unadressable) map given to UnmarshalTable. In this
- // (special) case, the map is used as-is instead of creating a new map.
- func unmarshalTable(cfg *Config, rv reflect.Value, t *ast.Table, toplevelMap bool) error {
- rv = indirect(rv)
- if err, ok := setUnmarshaler(cfg, rv, t); ok {
- return lineError(t.Line, err)
- }
- switch {
- case rv.Kind() == reflect.Struct:
- fc := makeFieldCache(cfg, rv.Type())
- for key, fieldAst := range t.Fields {
- fv, fieldName, err := fc.findField(cfg, rv, key)
- if err != nil {
- return lineError(fieldLineNumber(fieldAst), err)
- }
- if fv.IsValid() {
- if err := unmarshalField(cfg, fv, fieldAst); err != nil {
- return lineErrorField(fieldLineNumber(fieldAst), rv.Type().String()+"."+fieldName, err)
- }
- }
- }
- case rv.Kind() == reflect.Map || isEface(rv):
- m := rv
- if !toplevelMap {
- if rv.Kind() == reflect.Interface {
- m = reflect.ValueOf(make(map[string]interface{}))
- } else {
- m = reflect.MakeMap(rv.Type())
- }
- }
- elemtyp := m.Type().Elem()
- for key, fieldAst := range t.Fields {
- kv, err := unmarshalMapKey(m.Type().Key(), key)
- if err != nil {
- return lineError(fieldLineNumber(fieldAst), err)
- }
- fv := reflect.New(elemtyp).Elem()
- if err := unmarshalField(cfg, fv, fieldAst); err != nil {
- return lineError(fieldLineNumber(fieldAst), err)
- }
- m.SetMapIndex(kv, fv)
- }
- if !toplevelMap {
- rv.Set(m)
- }
- default:
- return lineError(t.Line, &unmarshalTypeError{"table", "struct or map", rv.Type()})
- }
- return nil
- }
- func fieldLineNumber(fieldAst interface{}) int {
- switch av := fieldAst.(type) {
- case *ast.KeyValue:
- return av.Line
- case *ast.Table:
- return av.Line
- case []*ast.Table:
- return av[0].Line
- default:
- panic(fmt.Sprintf("BUG: unhandled node type %T", fieldAst))
- }
- }
- func unmarshalField(cfg *Config, rv reflect.Value, fieldAst interface{}) error {
- switch av := fieldAst.(type) {
- case *ast.KeyValue:
- return setValue(cfg, rv, av.Value)
- case *ast.Table:
- return unmarshalTable(cfg, rv, av, false)
- case []*ast.Table:
- rv = indirect(rv)
- if err, ok := setUnmarshaler(cfg, rv, fieldAst); ok {
- return err
- }
- var slice reflect.Value
- switch {
- case rv.Kind() == reflect.Slice:
- slice = reflect.MakeSlice(rv.Type(), len(av), len(av))
- case isEface(rv):
- slice = reflect.ValueOf(make([]interface{}, len(av)))
- default:
- return &unmarshalTypeError{"array table", "slice", rv.Type()}
- }
- for i, tbl := range av {
- vv := reflect.New(slice.Type().Elem()).Elem()
- if err := unmarshalTable(cfg, vv, tbl, false); err != nil {
- return err
- }
- slice.Index(i).Set(vv)
- }
- rv.Set(slice)
- default:
- panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
- }
- return nil
- }
- func unmarshalMapKey(typ reflect.Type, key string) (reflect.Value, error) {
- rv := reflect.New(typ).Elem()
- if u, ok := rv.Addr().Interface().(encoding.TextUnmarshaler); ok {
- return rv, u.UnmarshalText([]byte(key))
- }
- switch typ.Kind() {
- case reflect.String:
- rv.SetString(key)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- i, err := strconv.ParseInt(key, 10, int(typ.Size()*8))
- if err != nil {
- return rv, convertNumError(typ.Kind(), err)
- }
- rv.SetInt(i)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- i, err := strconv.ParseUint(key, 10, int(typ.Size()*8))
- if err != nil {
- return rv, convertNumError(typ.Kind(), err)
- }
- rv.SetUint(i)
- default:
- return rv, fmt.Errorf("invalid map key type %s", typ)
- }
- return rv, nil
- }
- func setValue(cfg *Config, lhs reflect.Value, val ast.Value) error {
- lhs = indirect(lhs)
- if err, ok := setUnmarshaler(cfg, lhs, val); ok {
- return err
- }
- if err, ok := setTextUnmarshaler(lhs, val); ok {
- return err
- }
- switch v := val.(type) {
- case *ast.Integer:
- return setInt(lhs, v)
- case *ast.Float:
- return setFloat(lhs, v)
- case *ast.String:
- return setString(lhs, v)
- case *ast.Boolean:
- return setBoolean(lhs, v)
- case *ast.Datetime:
- return setDatetime(lhs, v)
- case *ast.Array:
- return setArray(cfg, lhs, v)
- default:
- panic(fmt.Sprintf("BUG: unhandled node type %T", v))
- }
- }
- func indirect(rv reflect.Value) reflect.Value {
- for rv.Kind() == reflect.Ptr {
- if rv.IsNil() {
- rv.Set(reflect.New(rv.Type().Elem()))
- }
- rv = rv.Elem()
- }
- return rv
- }
- func setUnmarshaler(cfg *Config, lhs reflect.Value, av interface{}) (error, bool) {
- if lhs.CanAddr() {
- if u, ok := lhs.Addr().Interface().(UnmarshalerRec); ok {
- err := u.UnmarshalTOML(func(v interface{}) error {
- return unmarshalTableOrValue(cfg, reflect.ValueOf(v), av)
- })
- return err, true
- }
- if u, ok := lhs.Addr().Interface().(Unmarshaler); ok {
- return u.UnmarshalTOML(unmarshalerSource(av)), true
- }
- }
- return nil, false
- }
- func unmarshalerSource(av interface{}) []byte {
- var source []byte
- switch av := av.(type) {
- case []*ast.Table:
- for i, tab := range av {
- source = append(source, tab.Source()...)
- if i != len(av)-1 {
- source = append(source, '\n')
- }
- }
- case ast.Value:
- source = []byte(av.Source())
- default:
- panic(fmt.Sprintf("BUG: unhandled node type %T", av))
- }
- return source
- }
- func setTextUnmarshaler(lhs reflect.Value, val ast.Value) (error, bool) {
- if !lhs.CanAddr() {
- return nil, false
- }
- u, ok := lhs.Addr().Interface().(encoding.TextUnmarshaler)
- if !ok || lhs.Type() == timeType {
- return nil, false
- }
- var data string
- switch val := val.(type) {
- case *ast.Array:
- return &unmarshalTypeError{"array", "", lhs.Type()}, true
- case *ast.String:
- data = val.Value
- default:
- data = val.Source()
- }
- return u.UnmarshalText([]byte(data)), true
- }
- func setInt(fv reflect.Value, v *ast.Integer) error {
- k := fv.Kind()
- switch {
- case k >= reflect.Int && k <= reflect.Int64:
- i, err := strconv.ParseInt(v.Value, 10, int(fv.Type().Size()*8))
- if err != nil {
- return convertNumError(fv.Kind(), err)
- }
- fv.SetInt(i)
- case k >= reflect.Uint && k <= reflect.Uintptr:
- i, err := strconv.ParseUint(v.Value, 10, int(fv.Type().Size()*8))
- if err != nil {
- return convertNumError(fv.Kind(), err)
- }
- fv.SetUint(i)
- case isEface(fv):
- i, err := strconv.ParseInt(v.Value, 10, 64)
- if err != nil {
- return convertNumError(reflect.Int64, err)
- }
- fv.Set(reflect.ValueOf(i))
- default:
- return &unmarshalTypeError{"integer", "", fv.Type()}
- }
- return nil
- }
- func setFloat(fv reflect.Value, v *ast.Float) error {
- f, err := v.Float()
- if err != nil {
- return err
- }
- switch {
- case fv.Kind() == reflect.Float32 || fv.Kind() == reflect.Float64:
- if fv.OverflowFloat(f) {
- return &overflowError{fv.Kind(), v.Value}
- }
- fv.SetFloat(f)
- case isEface(fv):
- fv.Set(reflect.ValueOf(f))
- default:
- return &unmarshalTypeError{"float", "", fv.Type()}
- }
- return nil
- }
- func setString(fv reflect.Value, v *ast.String) error {
- switch {
- case fv.Kind() == reflect.String:
- fv.SetString(v.Value)
- case isEface(fv):
- fv.Set(reflect.ValueOf(v.Value))
- default:
- return &unmarshalTypeError{"string", "", fv.Type()}
- }
- return nil
- }
- func setBoolean(fv reflect.Value, v *ast.Boolean) error {
- b, _ := v.Boolean()
- switch {
- case fv.Kind() == reflect.Bool:
- fv.SetBool(b)
- case isEface(fv):
- fv.Set(reflect.ValueOf(b))
- default:
- return &unmarshalTypeError{"boolean", "", fv.Type()}
- }
- return nil
- }
- func setDatetime(rv reflect.Value, v *ast.Datetime) error {
- t, err := v.Time()
- if err != nil {
- return err
- }
- if !timeType.AssignableTo(rv.Type()) {
- return &unmarshalTypeError{"datetime", "", rv.Type()}
- }
- rv.Set(reflect.ValueOf(t))
- return nil
- }
- func setArray(cfg *Config, rv reflect.Value, v *ast.Array) error {
- var slicetyp reflect.Type
- switch {
- case rv.Kind() == reflect.Slice:
- slicetyp = rv.Type()
- case isEface(rv):
- slicetyp = reflect.SliceOf(rv.Type())
- default:
- return &unmarshalTypeError{"array", "slice", rv.Type()}
- }
- if len(v.Value) == 0 {
- // Ensure defined slices are always set to a non-nil value.
- rv.Set(reflect.MakeSlice(slicetyp, 0, 0))
- return nil
- }
- tomltyp := reflect.TypeOf(v.Value[0])
- slice := reflect.MakeSlice(slicetyp, len(v.Value), len(v.Value))
- typ := slicetyp.Elem()
- for i, vv := range v.Value {
- if i > 0 && tomltyp != reflect.TypeOf(vv) {
- return errArrayMultiType
- }
- tmp := reflect.New(typ).Elem()
- if err := setValue(cfg, tmp, vv); err != nil {
- return err
- }
- slice.Index(i).Set(tmp)
- }
- rv.Set(slice)
- return nil
- }
- func isEface(rv reflect.Value) bool {
- return rv.Kind() == reflect.Interface && rv.Type().NumMethod() == 0
- }
|