cmd/geth: make TOML keys look exactly like Go struct fields

This commit is contained in:
Felix Lange 2017-04-11 00:56:56 +02:00
parent ee69b41d54
commit 075642f105
9 changed files with 192 additions and 89 deletions

View file

@ -20,8 +20,11 @@ import (
"bufio"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
"reflect"
"unicode"
cli "gopkg.in/urfave/cli.v1"
@ -62,6 +65,23 @@ var defaultNodeConfig = node.Config{
WSModules: []string{"eth", "net", "web3"},
}
// These settings ensure that TOML keys use the same names as Go struct fields.
var tomlSettings = toml.Config{
NormFieldName: func(rt reflect.Type, key string) string {
return key
},
FieldToKey: func(rt reflect.Type, field string) string {
return field
},
MissingField: func(rt reflect.Type, field string) error {
link := ""
if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
}
return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
},
}
type gethConfig struct {
Eth eth.Config
Node node.Config
@ -73,7 +93,8 @@ func loadConfig(file string, cfg *gethConfig) error {
return err
}
defer f.Close()
err = toml.NewDecoder(bufio.NewReader(f)).Decode(cfg)
err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg)
// Add file name to errors that have a line number.
if _, ok := err.(*toml.LineError); ok {
err = errors.New(file + ", " + err.Error())
@ -145,7 +166,7 @@ func dumpConfig(ctx *cli.Context) error {
comment += "# Note: this config doesn't contain the genesis block.\n\n"
}
out, err := toml.Marshal(&cfg)
out, err := tomlSettings.Marshal(&cfg)
if err != nil {
return err
}

View file

@ -97,7 +97,7 @@ type Config struct {
EthashDatasetsOnDisk int
// Gas Price Oracle options
GPO gasprice.Config `toml:"gpo"`
GPO gasprice.Config
// Enables tracking of SHA3 preimages in the VM
EnablePreimageRecording bool

View file

@ -33,7 +33,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
EthashDatasetDir string
EthashDatasetsInMem int
EthashDatasetsOnDisk int
GPO gasprice.Config `toml:"gpo"`
GPO gasprice.Config
EnablePreimageRecording bool
SolcPath string
DocRoot string `toml:"-"`
@ -94,7 +94,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
EthashDatasetDir *string
EthashDatasetsInMem *int
EthashDatasetsOnDisk *int
GPO *gasprice.Config `toml:"gpo"`
GPO *gasprice.Config
EnablePreimageRecording *bool
SolcPath *string
DocRoot *string `toml:"-"`

View file

@ -67,7 +67,7 @@ type Config struct {
DataDir string
// Configuration of peer-to-peer networking.
P2P p2p.Config `toml:"p2p"`
P2P p2p.Config
// KeyStoreDir is the file system folder that contains private keys. The directory can
// be specified as a relative path, in which case it is resolved relative to the
@ -80,51 +80,51 @@ type Config struct {
// UseLightweightKDF lowers the memory and CPU requirements of the key store
// scrypt KDF at the expense of security.
UseLightweightKDF bool `toml:"use_lightweight_kdf,omitempty"`
UseLightweightKDF bool `toml:",omitempty"`
// IPCPath is the requested location to place the IPC endpoint. If the path is
// a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
// relative), then that specific path is enforced. An empty path disables IPC.
IPCPath string `toml:"ipc_path,omitempty"`
IPCPath string `toml:",omitempty"`
// HTTPHost is the host interface on which to start the HTTP RPC server. If this
// field is empty, no HTTP API endpoint will be started.
HTTPHost string `toml:"http_host,omitempty"`
HTTPHost string `toml:",omitempty"`
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful
// for ephemeral nodes).
HTTPPort int `toml:"http_port,omitempty"`
HTTPPort int `toml:",omitempty"`
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
// clients. Please be aware that CORS is a browser enforced security, it's fully
// useless for custom HTTP clients.
HTTPCors string `toml:"http_cors,omitempty"`
HTTPCors string `toml:",omitempty"`
// HTTPModules is a list of API modules to expose via the HTTP RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
HTTPModules []string `toml:"http_modules,omitempty"`
HTTPModules []string `toml:",omitempty"`
// WSHost is the host interface on which to start the websocket RPC server. If
// this field is empty, no websocket API endpoint will be started.
WSHost string `toml:"ws_host,omitempty"`
WSHost string `toml:",omitempty"`
// WSPort is the TCP port number on which to start the websocket RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful for
// ephemeral nodes).
WSPort int `toml:"ws_port,omitempty"`
WSPort int `toml:",omitempty"`
// WSOrigins is the list of domain to accept websocket requests from. Please be
// aware that the server can only act upon the HTTP request the client sends and
// cannot verify the validity of the request header.
WSOrigins string `toml:"ws_origins,omitempty"`
WSOrigins string `toml:",omitempty"`
// WSModules is a list of API modules to expose via the websocket RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
WSModules []string `toml:"ws_modules,omitempty"`
WSModules []string `toml:",omitempty"`
}
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into

86
vendor/github.com/naoina/toml/config.go generated vendored Normal file
View file

@ -0,0 +1,86 @@
package toml
import (
"fmt"
"io"
"reflect"
"strings"
stringutil "github.com/naoina/go-stringutil"
"github.com/naoina/toml/ast"
)
// Config contains options for encoding and decoding.
type Config struct {
// NormFieldName is used to match TOML keys to struct fields. The function runs for
// both input keys and struct field names and should return a string that makes the
// two match. You must set this field to use the decoder.
//
// Example: The function in the default config removes _ and lowercases all keys. This
// allows a key called 'api_key' to match the struct field 'APIKey' because both are
// normalized to 'apikey'.
//
// Note that NormFieldName is not used for fields which define a TOML
// key through the struct tag.
NormFieldName func(typ reflect.Type, keyOrField string) string
// FieldToKey determines the TOML key of a struct field when encoding.
// You must set this field to use the encoder.
//
// Note that FieldToKey is not used for fields which define a TOML
// key through the struct tag.
FieldToKey func(typ reflect.Type, field string) string
// MissingField, if non-nil, is called when the decoder encounters a key for which no
// matching struct field exists. The default behavior is to return an error.
MissingField func(typ reflect.Type, key string) error
}
// DefaultConfig contains the default options for encoding and decoding.
// Snake case (i.e. 'foo_bar') is used for key names.
var DefaultConfig = Config{
NormFieldName: defaultNormFieldName,
FieldToKey: snakeCase,
}
func defaultNormFieldName(typ reflect.Type, s string) string {
return strings.Replace(strings.ToLower(s), "_", "", -1)
}
func snakeCase(typ reflect.Type, s string) string {
return stringutil.ToSnakeCase(s)
}
func defaultMissingField(typ reflect.Type, key string) error {
return fmt.Errorf("field corresponding to `%s' is not defined in %v", key, typ)
}
// NewEncoder returns a new Encoder that writes to w.
// It is shorthand for DefaultConfig.NewEncoder(w).
func NewEncoder(w io.Writer) *Encoder {
return DefaultConfig.NewEncoder(w)
}
// Marshal returns the TOML encoding of v.
// It is shorthand for DefaultConfig.Marshal(v).
func Marshal(v interface{}) ([]byte, error) {
return DefaultConfig.Marshal(v)
}
// Unmarshal parses the TOML data and stores the result in the value pointed to by v.
// It is shorthand for DefaultConfig.Unmarshal(data, v).
func Unmarshal(data []byte, v interface{}) error {
return DefaultConfig.Unmarshal(data, v)
}
// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
// It is shorthand for DefaultConfig.UnmarshalTable(t, v).
func UnmarshalTable(t *ast.Table, v interface{}) error {
return DefaultConfig.UnmarshalTable(t, v)
}
// NewDecoder returns a new Decoder that reads from r.
// It is shorthand for DefaultConfig.NewDecoder(r).
func NewDecoder(r io.Reader) *Decoder {
return DefaultConfig.NewDecoder(r)
}

View file

@ -47,12 +47,12 @@ var timeType = reflect.TypeOf(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 Unmarshal(data []byte, v interface{}) error {
func (cfg *Config) Unmarshal(data []byte, v interface{}) error {
table, err := Parse(data)
if err != nil {
return err
}
if err := UnmarshalTable(table, v); err != nil {
if err := cfg.UnmarshalTable(table, v); err != nil {
return err
}
return nil
@ -60,15 +60,14 @@ func Unmarshal(data []byte, v interface{}) error {
// A Decoder reads and decodes TOML from an input stream.
type Decoder struct {
r io.Reader
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 NewDecoder(r io.Reader) *Decoder {
return &Decoder{
r: r,
}
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.
@ -78,7 +77,7 @@ func (d *Decoder) Decode(v interface{}) error {
if err != nil {
return err
}
return Unmarshal(b, v)
return d.cfg.Unmarshal(b, v)
}
// UnmarshalerRec may be implemented by types to customize their behavior when being
@ -113,17 +112,17 @@ type Unmarshaler interface {
// TOML arrays to any type of slice
// TOML tables to struct or map
// TOML array tables to slice of struct or map
func UnmarshalTable(t *ast.Table, v interface{}) error {
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(rv, t, toplevelMap)
return unmarshalTable(cfg, rv, t, toplevelMap)
}
// used for UnmarshalerRec.
func unmarshalTableOrValue(rv reflect.Value, av interface{}) error {
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()}
}
@ -131,12 +130,12 @@ func unmarshalTableOrValue(rv reflect.Value, av interface{}) error {
switch av.(type) {
case *ast.KeyValue, *ast.Table, []*ast.Table:
if err := unmarshalField(rv, av); err != nil {
if err := unmarshalField(cfg, rv, av); err != nil {
return lineError(fieldLineNumber(av), err)
}
return nil
case ast.Value:
return setValue(rv, av.(ast.Value))
return setValue(cfg, rv, av.(ast.Value))
default:
panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
}
@ -146,21 +145,23 @@ func unmarshalTableOrValue(rv reflect.Value, av interface{}) error {
//
// 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(rv reflect.Value, t *ast.Table, toplevelMap bool) error {
func unmarshalTable(cfg *Config, rv reflect.Value, t *ast.Table, toplevelMap bool) error {
rv = indirect(rv)
if err, ok := setUnmarshaler(rv, t); ok {
if err, ok := setUnmarshaler(cfg, rv, t); ok {
return lineError(t.Line, err)
}
switch {
case rv.Kind() == reflect.Struct:
fc := makeFieldCache(rv.Type())
fc := makeFieldCache(cfg, rv.Type())
for key, fieldAst := range t.Fields {
fv, fieldName, err := fc.findField(rv, key)
fv, fieldName, err := fc.findField(cfg, rv, key)
if err != nil {
return lineError(fieldLineNumber(fieldAst), err)
}
if err := unmarshalField(fv, fieldAst); err != nil {
return lineErrorField(fieldLineNumber(fieldAst), rv.Type().String()+"."+fieldName, 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):
@ -179,7 +180,7 @@ func unmarshalTable(rv reflect.Value, t *ast.Table, toplevelMap bool) error {
return lineError(fieldLineNumber(fieldAst), err)
}
fv := reflect.New(elemtyp).Elem()
if err := unmarshalField(fv, fieldAst); err != nil {
if err := unmarshalField(cfg, fv, fieldAst); err != nil {
return lineError(fieldLineNumber(fieldAst), err)
}
m.SetMapIndex(kv, fv)
@ -206,15 +207,15 @@ func fieldLineNumber(fieldAst interface{}) int {
}
}
func unmarshalField(rv reflect.Value, fieldAst interface{}) error {
func unmarshalField(cfg *Config, rv reflect.Value, fieldAst interface{}) error {
switch av := fieldAst.(type) {
case *ast.KeyValue:
return setValue(rv, av.Value)
return setValue(cfg, rv, av.Value)
case *ast.Table:
return unmarshalTable(rv, av, false)
return unmarshalTable(cfg, rv, av, false)
case []*ast.Table:
rv = indirect(rv)
if err, ok := setUnmarshaler(rv, fieldAst); ok {
if err, ok := setUnmarshaler(cfg, rv, fieldAst); ok {
return err
}
var slice reflect.Value
@ -228,7 +229,7 @@ func unmarshalField(rv reflect.Value, fieldAst interface{}) error {
}
for i, tbl := range av {
vv := reflect.New(slice.Type().Elem()).Elem()
if err := unmarshalTable(vv, tbl, false); err != nil {
if err := unmarshalTable(cfg, vv, tbl, false); err != nil {
return err
}
slice.Index(i).Set(vv)
@ -266,9 +267,9 @@ func unmarshalMapKey(typ reflect.Type, key string) (reflect.Value, error) {
return rv, nil
}
func setValue(lhs reflect.Value, val ast.Value) error {
func setValue(cfg *Config, lhs reflect.Value, val ast.Value) error {
lhs = indirect(lhs)
if err, ok := setUnmarshaler(lhs, val); ok {
if err, ok := setUnmarshaler(cfg, lhs, val); ok {
return err
}
if err, ok := setTextUnmarshaler(lhs, val); ok {
@ -286,7 +287,7 @@ func setValue(lhs reflect.Value, val ast.Value) error {
case *ast.Datetime:
return setDatetime(lhs, v)
case *ast.Array:
return setArray(lhs, v)
return setArray(cfg, lhs, v)
default:
panic(fmt.Sprintf("BUG: unhandled node type %T", v))
}
@ -302,11 +303,11 @@ func indirect(rv reflect.Value) reflect.Value {
return rv
}
func setUnmarshaler(lhs reflect.Value, av interface{}) (error, bool) {
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(reflect.ValueOf(v), av)
return unmarshalTableOrValue(cfg, reflect.ValueOf(v), av)
})
return err, true
}
@ -438,7 +439,7 @@ func setDatetime(rv reflect.Value, v *ast.Datetime) error {
return nil
}
func setArray(rv reflect.Value, v *ast.Array) error {
func setArray(cfg *Config, rv reflect.Value, v *ast.Array) error {
var slicetyp reflect.Type
switch {
case rv.Kind() == reflect.Slice:
@ -463,7 +464,7 @@ func setArray(rv reflect.Value, v *ast.Array) error {
return errArrayMultiType
}
tmp := reflect.New(typ).Elem()
if err := setValue(tmp, vv); err != nil {
if err := setValue(cfg, tmp, vv); err != nil {
return err
}
slice.Index(i).Set(tmp)

View file

@ -10,7 +10,6 @@ import (
"strconv"
"time"
"github.com/naoina/go-stringutil"
"github.com/naoina/toml/ast"
)
@ -25,6 +24,7 @@ const (
// the TOML structure unless
// - the field's tag is "-", or
// - the field is empty and its tag specifies the "omitempty" option.
//
// The "toml" key in the struct field's tag value is the key name, followed by
// an optional comma and options. Examples:
//
@ -41,20 +41,21 @@ const (
// // Field appears in TOML as key "field", but the field is skipped if
// // empty. Note the leading comma.
// Field int `toml:",omitempty"`
func Marshal(v interface{}) ([]byte, error) {
func (cfg *Config) Marshal(v interface{}) ([]byte, error) {
buf := new(bytes.Buffer)
err := NewEncoder(buf).Encode(v)
err := cfg.NewEncoder(buf).Encode(v)
return buf.Bytes(), err
}
// A Encoder writes TOML to an output stream.
type Encoder struct {
w io.Writer
w io.Writer
cfg *Config
}
// NewEncoder returns a new Encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
func (cfg *Config) NewEncoder(w io.Writer) *Encoder {
return &Encoder{w, cfg}
}
// Encode writes the TOML of v to the stream.
@ -71,9 +72,9 @@ func (e *Encoder) Encode(v interface{}) error {
var err error
switch rv.Kind() {
case reflect.Struct:
err = buf.structFields(rv)
err = buf.structFields(e.cfg, rv)
case reflect.Map:
err = buf.mapFields(rv)
err = buf.mapFields(e.cfg, rv)
default:
err = &marshalTableError{rv.Type()}
}
@ -161,7 +162,7 @@ func (b *tableBuf) addChild(child *tableBuf) {
b.children = append(b.children, child)
}
func (b *tableBuf) structFields(rv reflect.Value) error {
func (b *tableBuf) structFields(cfg *Config, rv reflect.Value) error {
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
ft := rt.Field(i)
@ -177,9 +178,9 @@ func (b *tableBuf) structFields(rv reflect.Value) error {
continue
}
if name == "" {
name = stringutil.ToSnakeCase(ft.Name)
name = cfg.FieldToKey(rt, ft.Name)
}
if err := b.field(name, fv); err != nil {
if err := b.field(cfg, name, fv); err != nil {
return err
}
}
@ -195,7 +196,7 @@ func (l mapKeyList) Len() int { return len(l) }
func (l mapKeyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l mapKeyList) Less(i, j int) bool { return l[i].key < l[j].key }
func (b *tableBuf) mapFields(rv reflect.Value) error {
func (b *tableBuf) mapFields(cfg *Config, rv reflect.Value) error {
keys := rv.MapKeys()
keylist := make(mapKeyList, len(keys))
for i, key := range keys {
@ -209,18 +210,18 @@ func (b *tableBuf) mapFields(rv reflect.Value) error {
sort.Sort(keylist)
for _, kv := range keylist {
if err := b.field(kv.key, kv.value); err != nil {
if err := b.field(cfg, kv.key, kv.value); err != nil {
return err
}
}
return nil
}
func (b *tableBuf) field(name string, rv reflect.Value) error {
func (b *tableBuf) field(cfg *Config, name string, rv reflect.Value) error {
off := len(b.body)
b.body = append(b.body, quoteName(name)...)
b.body = append(b.body, " = "...)
isTable, err := b.value(rv, name)
isTable, err := b.value(cfg, rv, name)
if isTable {
b.body = b.body[:off] // rub out "key ="
} else {
@ -229,8 +230,8 @@ func (b *tableBuf) field(name string, rv reflect.Value) error {
return err
}
func (b *tableBuf) value(rv reflect.Value, name string) (bool, error) {
isMarshaler, isTable, err := b.marshaler(rv, name)
func (b *tableBuf) value(cfg *Config, rv reflect.Value, name string) (bool, error) {
isMarshaler, isTable, err := b.marshaler(cfg, rv, name)
if isMarshaler {
return isTable, err
}
@ -249,7 +250,7 @@ func (b *tableBuf) value(rv reflect.Value, name string) (bool, error) {
if rv.IsNil() {
return false, &marshalNilError{rv.Type()}
}
return b.value(rv.Elem(), name)
return b.value(cfg, rv.Elem(), name)
case reflect.Slice, reflect.Array:
rvlen := rv.Len()
if rvlen == 0 {
@ -261,7 +262,7 @@ func (b *tableBuf) value(rv reflect.Value, name string) (bool, error) {
wroteElem := false
b.body = append(b.body, '[')
for i := 0; i < rvlen; i++ {
isTable, err := b.value(rv.Index(i), name)
isTable, err := b.value(cfg, rv.Index(i), name)
if err != nil {
return isTable, err
}
@ -281,12 +282,12 @@ func (b *tableBuf) value(rv reflect.Value, name string) (bool, error) {
return !wroteElem, nil
case reflect.Struct:
child := b.newChild(name)
err := child.structFields(rv)
err := child.structFields(cfg, rv)
b.addChild(child)
return true, err
case reflect.Map:
child := b.newChild(name)
err := child.mapFields(rv)
err := child.mapFields(cfg, rv)
b.addChild(child)
return true, err
default:
@ -295,7 +296,7 @@ func (b *tableBuf) value(rv reflect.Value, name string) (bool, error) {
return false, nil
}
func (b *tableBuf) marshaler(rv reflect.Value, name string) (handled, isTable bool, err error) {
func (b *tableBuf) marshaler(cfg *Config, rv reflect.Value, name string) (handled, isTable bool, err error) {
switch t := rv.Interface().(type) {
case encoding.TextMarshaler:
enc, err := t.MarshalText()
@ -309,7 +310,7 @@ func (b *tableBuf) marshaler(rv reflect.Value, name string) (handled, isTable bo
if err != nil {
return true, false, err
}
isTable, err = b.value(reflect.ValueOf(newval), name)
isTable, err = b.value(cfg, reflect.ValueOf(newval), name)
return true, isTable, err
case Marshaler:
enc, err := t.MarshalTOML()

View file

@ -20,7 +20,7 @@ type fieldInfo struct {
ignored bool
}
func makeFieldCache(rt reflect.Type) fieldCache {
func makeFieldCache(cfg *Config, rt reflect.Type) fieldCache {
named, auto := make(map[string]fieldInfo), make(map[string]fieldInfo)
for i := 0; i < rt.NumField(); i++ {
ft := rt.Field(i)
@ -31,7 +31,7 @@ func makeFieldCache(rt reflect.Type) fieldCache {
col, _ := extractTag(ft.Tag.Get(fieldTagName))
info := fieldInfo{index: ft.Index, name: ft.Name, ignored: col == "-"}
if col == "" || col == "-" {
auto[normFieldName(ft.Name)] = info
auto[cfg.NormFieldName(rt, ft.Name)] = info
} else {
named[col] = info
}
@ -39,23 +39,23 @@ func makeFieldCache(rt reflect.Type) fieldCache {
return fieldCache{named, auto}
}
func (fc fieldCache) findField(rv reflect.Value, name string) (reflect.Value, string, error) {
func (fc fieldCache) findField(cfg *Config, rv reflect.Value, name string) (reflect.Value, string, error) {
info, found := fc.named[name]
if !found {
info, found = fc.auto[normFieldName(name)]
info, found = fc.auto[cfg.NormFieldName(rv.Type(), name)]
}
if !found {
return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' is not defined in %v", name, rv.Type())
if cfg.MissingField == nil {
return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' is not defined in %v", name, rv.Type())
} else {
return reflect.Value{}, "", cfg.MissingField(rv.Type(), name)
}
} else if info.ignored {
return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' in %v cannot be set through TOML", name, rv.Type())
}
return rv.FieldByIndex(info.index), info.name, nil
}
func normFieldName(s string) string {
return strings.Replace(strings.ToLower(s), "_", "", -1)
}
func extractTag(tag string) (col, rest string) {
tags := strings.SplitN(tag, ",", 2)
if len(tags) == 2 {

12
vendor/vendor.json vendored
View file

@ -202,16 +202,10 @@
"revisionTime": "2015-03-14T17:03:34Z"
},
{
"checksumSHA1": "A6nZdZ1/lTOVINhIndyw2ZWN9JU=",
"path": "github.com/naoina/go-stringutil",
"revision": "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b",
"revisionTime": "2015-11-18T23:44:43Z"
},
{
"checksumSHA1": "qzMyvEe802B17Amr94Lq2KvFif4=",
"checksumSHA1": "2gmvVTDCks8cPhpmyDlvm0sbrXE=",
"path": "github.com/naoina/toml",
"revision": "5fabdde119404993567f090673d9fb7b05b57833",
"revisionTime": "2017-04-05T10:49:32Z"
"revision": "ac014c6b6502388d89a85552b7208b8da7cfe104",
"revisionTime": "2017-04-10T21:57:17Z"
},
{
"checksumSHA1": "xZBlSMT5o/A+EDOro6KbfHZwSNc=",