Skip to content

Commit 19eb8cf

Browse files
authored
Fix various quoted keys bugs (#400)
Fixes #396 #397 #398 #399
1 parent c5fbd3e commit 19eb8cf

File tree

8 files changed

+82
-33
lines changed

8 files changed

+82
-33
lines changed

keysparsing.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package toml
55
import (
66
"errors"
77
"fmt"
8-
"unicode"
98
)
109

1110
// Convert the bare key group string to an array.
@@ -109,5 +108,5 @@ func parseKey(key string) ([]string, error) {
109108
}
110109

111110
func isValidBareChar(r rune) bool {
112-
return isAlphanumeric(r) || r == '-' || unicode.IsNumber(r)
111+
return isAlphanumeric(r) || r == '-' || isDigit(r)
113112
}

lexer_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ func TestBasicKeyWithUppercaseMix(t *testing.T) {
105105
}
106106

107107
func TestBasicKeyWithInternationalCharacters(t *testing.T) {
108-
testFlow(t, "héllÖ", []token{
109-
{Position{1, 1}, tokenKey, "héllÖ"},
110-
{Position{1, 6}, tokenEOF, ""},
108+
testFlow(t, "'héllÖ'", []token{
109+
{Position{1, 1}, tokenKey, "'héllÖ'"},
110+
{Position{1, 8}, tokenEOF, ""},
111111
})
112112
}
113113

marshal.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
436436
if tree, ok := val.(*Tree); ok && mtypef.Anonymous && !opts.nameFromTag && !e.promoteAnon {
437437
e.appendTree(tval, tree)
438438
} else {
439-
tval.SetWithOptions(opts.name, SetOptions{
439+
tval.SetPathWithOptions([]string{opts.name}, SetOptions{
440440
Comment: opts.comment,
441441
Commented: opts.commented,
442442
Multiline: opts.multiline,
@@ -481,7 +481,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
481481
}
482482
tval.SetPath([]string{keyStr}, val)
483483
} else {
484-
tval.Set(key.String(), val)
484+
tval.SetPath([]string{key.String()}, val)
485485
}
486486
}
487487
}
@@ -754,17 +754,17 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.V
754754
found := false
755755
if tval != nil {
756756
for _, key := range keysToTry {
757-
exists := tval.Has(key)
757+
exists := tval.HasPath([]string{key})
758758
if !exists {
759759
continue
760760
}
761761

762762
d.visitor.push(key)
763-
val := tval.Get(key)
763+
val := tval.GetPath([]string{key})
764764
fval := mval.Field(i)
765765
mvalf, err := d.valueFromToml(mtypef.Type, val, &fval)
766766
if err != nil {
767-
return mval, formatError(err, tval.GetPosition(key))
767+
return mval, formatError(err, tval.GetPositionPath([]string{key}))
768768
}
769769
mval.Field(i).Set(mvalf)
770770
found = true
@@ -838,7 +838,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.V
838838
val := tval.GetPath([]string{key})
839839
mvalf, err := d.valueFromToml(mtype.Elem(), val, nil)
840840
if err != nil {
841-
return mval, formatError(err, tval.GetPosition(key))
841+
return mval, formatError(err, tval.GetPositionPath([]string{key}))
842842
}
843843
mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf)
844844
d.visitor.pop()

marshal_test.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,59 @@ func TestBasicUnmarshal(t *testing.T) {
287287
}
288288
}
289289

290+
type quotedKeyMarshalTestStruct struct {
291+
String string `toml:"Z.string-àéù"`
292+
Float float64 `toml:"Yfloat-𝟘"`
293+
Sub basicMarshalTestSubStruct `toml:"Xsubdoc-àéù"`
294+
SubList []basicMarshalTestSubStruct `toml:"W.sublist-𝟘"`
295+
}
296+
297+
var quotedKeyMarshalTestData = quotedKeyMarshalTestStruct{
298+
String: "Hello",
299+
Float: 3.5,
300+
Sub: basicMarshalTestSubStruct{"One"},
301+
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
302+
}
303+
304+
var quotedKeyMarshalTestToml = []byte(`"Yfloat-𝟘" = 3.5
305+
"Z.string-àéù" = "Hello"
306+
307+
[["W.sublist-𝟘"]]
308+
String2 = "Two"
309+
310+
[["W.sublist-𝟘"]]
311+
String2 = "Three"
312+
313+
["Xsubdoc-àéù"]
314+
String2 = "One"
315+
`)
316+
317+
func TestBasicMarshalQuotedKey(t *testing.T) {
318+
result, err := Marshal(quotedKeyMarshalTestData)
319+
if err != nil {
320+
t.Fatal(err)
321+
}
322+
expected := quotedKeyMarshalTestToml
323+
if !bytes.Equal(result, expected) {
324+
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
325+
}
326+
}
327+
328+
func TestBasicUnmarshalQuotedKey(t *testing.T) {
329+
tree, err := LoadBytes(quotedKeyMarshalTestToml)
330+
if err != nil {
331+
t.Fatal(err)
332+
}
333+
334+
var q quotedKeyMarshalTestStruct
335+
tree.Unmarshal(&q)
336+
fmt.Println(q)
337+
338+
if !reflect.DeepEqual(quotedKeyMarshalTestData, q) {
339+
t.Errorf("Bad unmarshal: expected\n-----\n%v\n-----\ngot\n-----\n%v\n-----\n", quotedKeyMarshalTestData, q)
340+
}
341+
}
342+
290343
type testDoc struct {
291344
Title string `toml:"title"`
292345
BasicLists testDocBasicLists `toml:"basic_lists"`
@@ -2070,7 +2123,6 @@ func TestUnmarshalCamelCaseKey(t *testing.T) {
20702123
}
20712124
}
20722125

2073-
20742126
func TestUnmarshalNegativeUint(t *testing.T) {
20752127
type check struct{ U uint }
20762128

query/match.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package query
22

33
import (
44
"fmt"
5+
56
"github.com/pelletier/go-toml"
67
)
78

@@ -44,16 +45,16 @@ func newMatchKeyFn(name string) *matchKeyFn {
4445
func (f *matchKeyFn) call(node interface{}, ctx *queryContext) {
4546
if array, ok := node.([]*toml.Tree); ok {
4647
for _, tree := range array {
47-
item := tree.Get(f.Name)
48+
item := tree.GetPath([]string{f.Name})
4849
if item != nil {
49-
ctx.lastPosition = tree.GetPosition(f.Name)
50+
ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
5051
f.next.call(item, ctx)
5152
}
5253
}
5354
} else if tree, ok := node.(*toml.Tree); ok {
54-
item := tree.Get(f.Name)
55+
item := tree.GetPath([]string{f.Name})
5556
if item != nil {
56-
ctx.lastPosition = tree.GetPosition(f.Name)
57+
ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
5758
f.next.call(item, ctx)
5859
}
5960
}
@@ -129,8 +130,8 @@ func newMatchAnyFn() *matchAnyFn {
129130
func (f *matchAnyFn) call(node interface{}, ctx *queryContext) {
130131
if tree, ok := node.(*toml.Tree); ok {
131132
for _, k := range tree.Keys() {
132-
v := tree.Get(k)
133-
ctx.lastPosition = tree.GetPosition(k)
133+
v := tree.GetPath([]string{k})
134+
ctx.lastPosition = tree.GetPositionPath([]string{k})
134135
f.next.call(v, ctx)
135136
}
136137
}
@@ -168,8 +169,8 @@ func (f *matchRecursiveFn) call(node interface{}, ctx *queryContext) {
168169
var visit func(tree *toml.Tree)
169170
visit = func(tree *toml.Tree) {
170171
for _, k := range tree.Keys() {
171-
v := tree.Get(k)
172-
ctx.lastPosition = tree.GetPosition(k)
172+
v := tree.GetPath([]string{k})
173+
ctx.lastPosition = tree.GetPositionPath([]string{k})
173174
f.next.call(v, ctx)
174175
switch node := v.(type) {
175176
case *toml.Tree:
@@ -207,9 +208,9 @@ func (f *matchFilterFn) call(node interface{}, ctx *queryContext) {
207208
switch castNode := node.(type) {
208209
case *toml.Tree:
209210
for _, k := range castNode.Keys() {
210-
v := castNode.Get(k)
211+
v := castNode.GetPath([]string{k})
211212
if fn(v) {
212-
ctx.lastPosition = castNode.GetPosition(k)
213+
ctx.lastPosition = castNode.GetPositionPath([]string{k})
213214
f.next.call(v, ctx)
214215
}
215216
}

query/tokens.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package query
22

33
import (
44
"fmt"
5-
"github.com/pelletier/go-toml"
65
"strconv"
7-
"unicode"
6+
7+
"github.com/pelletier/go-toml"
88
)
99

1010
// Define tokens
@@ -92,11 +92,11 @@ func isSpace(r rune) bool {
9292
}
9393

9494
func isAlphanumeric(r rune) bool {
95-
return unicode.IsLetter(r) || r == '_'
95+
return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_'
9696
}
9797

9898
func isDigit(r rune) bool {
99-
return unicode.IsNumber(r)
99+
return '0' <= r && r <= '9'
100100
}
101101

102102
func isHexDigit(r rune) bool {

token.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package toml
22

3-
import (
4-
"fmt"
5-
"unicode"
6-
)
3+
import "fmt"
74

85
// Define tokens
96
type tokenType int
@@ -112,7 +109,7 @@ func isSpace(r rune) bool {
112109
}
113110

114111
func isAlphanumeric(r rune) bool {
115-
return unicode.IsLetter(r) || r == '_'
112+
return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_'
116113
}
117114

118115
func isKeyChar(r rune) bool {
@@ -127,7 +124,7 @@ func isKeyStartChar(r rune) bool {
127124
}
128125

129126
func isDigit(r rune) bool {
130-
return unicode.IsNumber(r)
127+
return '0' <= r && r <= '9'
131128
}
132129

133130
func isHexDigit(r rune) bool {

tomltree_write.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i
338338
k := node.key
339339
v := t.values[k]
340340

341-
combinedKey := k
341+
combinedKey := quoteKeyIfNeeded(k)
342342
if keyspace != "" {
343343
combinedKey = keyspace + "." + combinedKey
344344
}

0 commit comments

Comments
 (0)