util.go 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. package toml
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. )
  7. const fieldTagName = "toml"
  8. // fieldCache maps normalized field names to their position in a struct.
  9. type fieldCache struct {
  10. named map[string]fieldInfo // fields with an explicit name in tag
  11. auto map[string]fieldInfo // fields with auto-assigned normalized names
  12. }
  13. type fieldInfo struct {
  14. index []int
  15. name string
  16. ignored bool
  17. }
  18. func makeFieldCache(cfg *Config, rt reflect.Type) fieldCache {
  19. named, auto := make(map[string]fieldInfo), make(map[string]fieldInfo)
  20. for i := 0; i < rt.NumField(); i++ {
  21. ft := rt.Field(i)
  22. // skip unexported fields
  23. if ft.PkgPath != "" && !ft.Anonymous {
  24. continue
  25. }
  26. col, _ := extractTag(ft.Tag.Get(fieldTagName))
  27. info := fieldInfo{index: ft.Index, name: ft.Name, ignored: col == "-"}
  28. if col == "" || col == "-" {
  29. auto[cfg.NormFieldName(rt, ft.Name)] = info
  30. } else {
  31. named[col] = info
  32. }
  33. }
  34. return fieldCache{named, auto}
  35. }
  36. func (fc fieldCache) findField(cfg *Config, rv reflect.Value, name string) (reflect.Value, string, error) {
  37. info, found := fc.named[name]
  38. if !found {
  39. info, found = fc.auto[cfg.NormFieldName(rv.Type(), name)]
  40. }
  41. if !found {
  42. if cfg.MissingField == nil {
  43. return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' is not defined in %v", name, rv.Type())
  44. } else {
  45. return reflect.Value{}, "", cfg.MissingField(rv.Type(), name)
  46. }
  47. } else if info.ignored {
  48. return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' in %v cannot be set through TOML", name, rv.Type())
  49. }
  50. return rv.FieldByIndex(info.index), info.name, nil
  51. }
  52. func extractTag(tag string) (col, rest string) {
  53. tags := strings.SplitN(tag, ",", 2)
  54. if len(tags) == 2 {
  55. return strings.TrimSpace(tags[0]), strings.TrimSpace(tags[1])
  56. }
  57. return strings.TrimSpace(tags[0]), ""
  58. }