From 60801d0059e5ac0eb04c240af09d742f32c11ccc Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Mon, 5 Jun 2023 03:01:08 +0200 Subject: [PATCH] Correctly set the Meta.Keys() order for inline tables The table name would be listed *after* the key name; e.g. ./cmd/tomlv would print: toml-schema.version Integer toml-schema Hash For the document: toml-schema = {version = 1} With this it prints the expected: toml-schema Hash toml-schema.version Integer --- decode.go | 2 +- decode_test.go | 30 ++++++++++++++++++++++++++++++ encode.go | 2 +- parse.go | 18 +++++++----------- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/decode.go b/decode.go index dac2dc28..4d38f3bf 100644 --- a/decode.go +++ b/decode.go @@ -248,7 +248,7 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error { case reflect.Bool: return md.unifyBool(data, rv) case reflect.Interface: - if rv.NumMethod() > 0 { // Only support empty interfaces are supported. + if rv.NumMethod() > 0 { /// Only empty interfaces are supported. return md.e("unsupported type %s", rv.Type()) } return md.unifyAnything(data, rv) diff --git a/decode_test.go b/decode_test.go index bfe7c4a7..6ca4a06f 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1171,6 +1171,36 @@ func TestDecodeDoubleTags(t *testing.T) { } } +func TestMetaKeys(t *testing.T) { + tests := []struct { + in string + want []Key + }{ + {"", []Key{}}, + {"b=1\na=1", []Key{Key{"b"}, Key{"a"}}}, + {"a.b=1\na.a=1", []Key{Key{"a", "b"}, Key{"a", "a"}}}, // TODO: should include "a" + {"[tbl]\na=1", []Key{Key{"tbl"}, Key{"tbl", "a"}}}, + {"[tbl]\na.a=1", []Key{Key{"tbl"}, Key{"tbl", "a", "a"}}}, // TODO: should include "a.a" + {"tbl={a=1}", []Key{Key{"tbl"}, Key{"tbl", "a"}}}, + {"tbl={a={b=1}}", []Key{Key{"tbl"}, Key{"tbl", "a"}, Key{"tbl", "a", "b"}}}, + } + + for _, tt := range tests { + t.Run("", func(t *testing.T) { + var x interface{} + meta, err := Decode(tt.in, &x) + if err != nil { + t.Fatal(err) + } + + have := meta.Keys() + if !reflect.DeepEqual(tt.want, have) { + t.Errorf("\nhave: %s\nwant: %s\n", have, tt.want) + } + }) + } +} + // errorContains checks if the error message in have contains the text in // want. // diff --git a/encode.go b/encode.go index 13238bf9..9cd25d75 100644 --- a/encode.go +++ b/encode.go @@ -503,7 +503,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { fieldVal = eindirect(fieldVal) - if isNil(fieldVal) { // Don't write anything for nil fields. + if isNil(fieldVal) { /// Don't write anything for nil fields. continue } diff --git a/parse.go b/parse.go index a4e74bfe..2aeb4f16 100644 --- a/parse.go +++ b/parse.go @@ -203,12 +203,12 @@ func (p *parser) topLevel(item item) { for i := range context { p.addImplicitContext(append(p.context, context[i:i+1]...)) } + p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Set value. vItem := p.next() val, typ := p.value(vItem, false) p.set(p.currentKey, val, typ, vItem.pos) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Remove the context we added (preserving any context from [tbl] lines). p.context = outerContext @@ -445,11 +445,11 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom for i := range context { p.addImplicitContext(append(p.context, context[i:i+1]...)) } + p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Set the value. val, typ := p.value(p.next(), false) p.set(p.currentKey, val, typ, it.pos) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) hash[p.currentKey] = val /// Restore context. @@ -570,7 +570,6 @@ func (p *parser) addContext(key Key, array bool) { func (p *parser) set(key string, val interface{}, typ tomlType, pos Position) { p.setValue(key, val) p.setType(key, typ, pos) - } // setValue sets the given key to the given value in the current context. @@ -651,14 +650,11 @@ func (p *parser) setType(key string, typ tomlType, pos Position) { // Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and // "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly). -func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } -func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } -func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } -func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray } -func (p *parser) addImplicitContext(key Key) { - p.addImplicit(key) - p.addContext(key, false) -} +func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } +func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } +func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } +func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray } +func (p *parser) addImplicitContext(key Key) { p.addImplicit(key); p.addContext(key, false) } // current returns the full key name of the current context. func (p *parser) current() string {