func (p *toml) lookupTable(t *ast.Table, keys []string) (*ast.Table, error) { for _, s := range keys { val, exists := t.Fields[s] if !exists { tbl := &ast.Table{ Line: p.line, Name: s, Type: ast.TableTypeNormal, } if t.Fields == nil { t.Fields = make(map[string]interface{}) } t.Fields[s] = tbl t = tbl continue } switch v := val.(type) { case *ast.Table: t = v case []*ast.Table: t = v[len(v)-1] case *ast.KeyValue: return nil, fmt.Errorf("key `%s' is in conflict with line %d", s, v.Line) default: return nil, fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", s, v) } } return t, nil }
func (p *toml) setArrayTable(t *ast.Table, buf []rune, begin, end int) { name := string(buf[begin:end]) if t, exists := p.tableMap[name]; exists && t.Type == ast.TableTypeNormal { p.Error(fmt.Errorf("table `%s' is in conflict with %v table in line %d", name, t.Type, t.Line)) } names := splitTableKey(name) t, err := p.lookupTable(t, names[:len(names)-1]) if err != nil { p.Error(err) } last := names[len(names)-1] tbl := &ast.Table{ Position: ast.Position{begin, end}, Line: p.line, Name: last, Type: ast.TableTypeArray, } switch v := t.Fields[last].(type) { case nil: if t.Fields == nil { t.Fields = make(map[string]interface{}) } t.Fields[last] = []*ast.Table{tbl} case []*ast.Table: t.Fields[last] = append(v, tbl) case *ast.KeyValue: p.Error(fmt.Errorf("key `%s' is in conflict with line %d", last, v.Line)) default: p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", last, v)) } p.currentTable = tbl p.tableMap[name] = p.currentTable }
// 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 or []interface{} // TOML tables to struct // TOML array of tables to slice of struct func UnmarshalTable(t *ast.Table, v interface{}) (err error) { if v == nil { return fmt.Errorf("v must not be nil") } rv := reflect.ValueOf(v) if kind := rv.Kind(); kind != reflect.Ptr && kind != reflect.Map { return fmt.Errorf("v must be a pointer or map") } for rv.Kind() == reflect.Ptr { rv = rv.Elem() } if err, ok := setUnmarshaler(rv, string(t.Data)); ok { return err } for key, val := range t.Fields { switch av := val.(type) { case *ast.KeyValue: fv, fieldName, found := findField(rv, key) if !found { return fmt.Errorf("line %d: field corresponding to `%s' is not defined in `%T'", av.Line, key, v) } switch fv.Kind() { case reflect.Map: mv := reflect.New(fv.Type().Elem()).Elem() if err := UnmarshalTable(t, mv.Addr().Interface()); err != nil { return err } fv.SetMapIndex(reflect.ValueOf(fieldName), mv) default: if err := setValue(fv, av.Value); err != nil { return fmt.Errorf("line %d: %v.%s: %v", av.Line, rv.Type(), fieldName, err) } if rv.Kind() == reflect.Map { rv.SetMapIndex(reflect.ValueOf(fieldName), fv) } } case *ast.Table: fv, fieldName, found := findField(rv, key) if !found { return fmt.Errorf("line %d: field corresponding to `%s' is not defined in `%T'", av.Line, key, v) } if err, ok := setUnmarshaler(fv, string(av.Data)); ok { if err != nil { return err } continue } for fv.Kind() == reflect.Ptr { fv.Set(reflect.New(fv.Type().Elem())) fv = fv.Elem() } switch fv.Kind() { case reflect.Struct: vv := reflect.New(fv.Type()).Elem() if err := UnmarshalTable(av, vv.Addr().Interface()); err != nil { return err } fv.Set(vv) if rv.Kind() == reflect.Map { rv.SetMapIndex(reflect.ValueOf(fieldName), fv) } case reflect.Map: mv := reflect.MakeMap(fv.Type()) if err := UnmarshalTable(av, mv.Interface()); err != nil { return err } fv.Set(mv) default: return fmt.Errorf("line %d: `%v.%s' must be struct or map, but %v given", av.Line, rv.Type(), fieldName, fv.Kind()) } case []*ast.Table: fv, fieldName, found := findField(rv, key) if !found { return fmt.Errorf("line %d: field corresponding to `%s' is not defined in `%T'", av[0].Line, key, v) } data := make([]string, 0, len(av)) for _, tbl := range av { data = append(data, string(tbl.Data)) } if err, ok := setUnmarshaler(fv, strings.Join(data, "\n")); ok { if err != nil { return err } continue } t := fv.Type().Elem() pc := 0 for ; t.Kind() == reflect.Ptr; pc++ { t = t.Elem() } if fv.Kind() != reflect.Slice { return fmt.Errorf("line %d: `%v.%s' must be slice type, but %v given", av[0].Line, rv.Type(), fieldName, fv.Kind()) } for _, tbl := range av { var vv reflect.Value switch t.Kind() { case reflect.Map: vv = reflect.MakeMap(t) if err := UnmarshalTable(tbl, vv.Interface()); err != nil { return err } default: vv = reflect.New(t).Elem() if err := UnmarshalTable(tbl, vv.Addr().Interface()); err != nil { return err } } for i := 0; i < pc; i++ { vv = vv.Addr() pv := reflect.New(vv.Type()).Elem() pv.Set(vv) vv = pv } fv.Set(reflect.Append(fv, vv)) } if rv.Kind() == reflect.Map { rv.SetMapIndex(reflect.ValueOf(fieldName), fv) } default: return fmt.Errorf("BUG: unknown type `%T'", t) } } return nil }