Fix various quoted keys bugs (#400)

Fixes #396 #397 #398 #399
This commit is contained in:
x-hgg-x
2020-05-07 05:13:18 +02:00
committed by GitHub
parent c5fbd3eba6
commit 19eb8cf036
8 changed files with 82 additions and 33 deletions
+1 -2
View File
@@ -5,7 +5,6 @@ package toml
import ( import (
"errors" "errors"
"fmt" "fmt"
"unicode"
) )
// Convert the bare key group string to an array. // Convert the bare key group string to an array.
@@ -109,5 +108,5 @@ func parseKey(key string) ([]string, error) {
} }
func isValidBareChar(r rune) bool { func isValidBareChar(r rune) bool {
return isAlphanumeric(r) || r == '-' || unicode.IsNumber(r) return isAlphanumeric(r) || r == '-' || isDigit(r)
} }
+3 -3
View File
@@ -105,9 +105,9 @@ func TestBasicKeyWithUppercaseMix(t *testing.T) {
} }
func TestBasicKeyWithInternationalCharacters(t *testing.T) { func TestBasicKeyWithInternationalCharacters(t *testing.T) {
testFlow(t, "héllÖ", []token{ testFlow(t, "'héllÖ'", []token{
{Position{1, 1}, tokenKey, "héllÖ"}, {Position{1, 1}, tokenKey, "'héllÖ'"},
{Position{1, 6}, tokenEOF, ""}, {Position{1, 8}, tokenEOF, ""},
}) })
} }
+6 -6
View File
@@ -436,7 +436,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
if tree, ok := val.(*Tree); ok && mtypef.Anonymous && !opts.nameFromTag && !e.promoteAnon { if tree, ok := val.(*Tree); ok && mtypef.Anonymous && !opts.nameFromTag && !e.promoteAnon {
e.appendTree(tval, tree) e.appendTree(tval, tree)
} else { } else {
tval.SetWithOptions(opts.name, SetOptions{ tval.SetPathWithOptions([]string{opts.name}, SetOptions{
Comment: opts.comment, Comment: opts.comment,
Commented: opts.commented, Commented: opts.commented,
Multiline: opts.multiline, Multiline: opts.multiline,
@@ -481,7 +481,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
} }
tval.SetPath([]string{keyStr}, val) tval.SetPath([]string{keyStr}, val)
} else { } else {
tval.Set(key.String(), val) tval.SetPath([]string{key.String()}, val)
} }
} }
} }
@@ -754,17 +754,17 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.V
found := false found := false
if tval != nil { if tval != nil {
for _, key := range keysToTry { for _, key := range keysToTry {
exists := tval.Has(key) exists := tval.HasPath([]string{key})
if !exists { if !exists {
continue continue
} }
d.visitor.push(key) d.visitor.push(key)
val := tval.Get(key) val := tval.GetPath([]string{key})
fval := mval.Field(i) fval := mval.Field(i)
mvalf, err := d.valueFromToml(mtypef.Type, val, &fval) mvalf, err := d.valueFromToml(mtypef.Type, val, &fval)
if err != nil { if err != nil {
return mval, formatError(err, tval.GetPosition(key)) return mval, formatError(err, tval.GetPositionPath([]string{key}))
} }
mval.Field(i).Set(mvalf) mval.Field(i).Set(mvalf)
found = true found = true
@@ -838,7 +838,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.V
val := tval.GetPath([]string{key}) val := tval.GetPath([]string{key})
mvalf, err := d.valueFromToml(mtype.Elem(), val, nil) mvalf, err := d.valueFromToml(mtype.Elem(), val, nil)
if err != nil { if err != nil {
return mval, formatError(err, tval.GetPosition(key)) return mval, formatError(err, tval.GetPositionPath([]string{key}))
} }
mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf) mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf)
d.visitor.pop() d.visitor.pop()
+53 -1
View File
@@ -287,6 +287,59 @@ func TestBasicUnmarshal(t *testing.T) {
} }
} }
type quotedKeyMarshalTestStruct struct {
String string `toml:"Z.string-àéù"`
Float float64 `toml:"Yfloat-𝟘"`
Sub basicMarshalTestSubStruct `toml:"Xsubdoc-àéù"`
SubList []basicMarshalTestSubStruct `toml:"W.sublist-𝟘"`
}
var quotedKeyMarshalTestData = quotedKeyMarshalTestStruct{
String: "Hello",
Float: 3.5,
Sub: basicMarshalTestSubStruct{"One"},
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
}
var quotedKeyMarshalTestToml = []byte(`"Yfloat-𝟘" = 3.5
"Z.string-àéù" = "Hello"
[["W.sublist-𝟘"]]
String2 = "Two"
[["W.sublist-𝟘"]]
String2 = "Three"
["Xsubdoc-àéù"]
String2 = "One"
`)
func TestBasicMarshalQuotedKey(t *testing.T) {
result, err := Marshal(quotedKeyMarshalTestData)
if err != nil {
t.Fatal(err)
}
expected := quotedKeyMarshalTestToml
if !bytes.Equal(result, expected) {
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
}
}
func TestBasicUnmarshalQuotedKey(t *testing.T) {
tree, err := LoadBytes(quotedKeyMarshalTestToml)
if err != nil {
t.Fatal(err)
}
var q quotedKeyMarshalTestStruct
tree.Unmarshal(&q)
fmt.Println(q)
if !reflect.DeepEqual(quotedKeyMarshalTestData, q) {
t.Errorf("Bad unmarshal: expected\n-----\n%v\n-----\ngot\n-----\n%v\n-----\n", quotedKeyMarshalTestData, q)
}
}
type testDoc struct { type testDoc struct {
Title string `toml:"title"` Title string `toml:"title"`
BasicLists testDocBasicLists `toml:"basic_lists"` BasicLists testDocBasicLists `toml:"basic_lists"`
@@ -2070,7 +2123,6 @@ func TestUnmarshalCamelCaseKey(t *testing.T) {
} }
} }
func TestUnmarshalNegativeUint(t *testing.T) { func TestUnmarshalNegativeUint(t *testing.T) {
type check struct{ U uint } type check struct{ U uint }
+11 -10
View File
@@ -2,6 +2,7 @@ package query
import ( import (
"fmt" "fmt"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
) )
@@ -44,16 +45,16 @@ func newMatchKeyFn(name string) *matchKeyFn {
func (f *matchKeyFn) call(node interface{}, ctx *queryContext) { func (f *matchKeyFn) call(node interface{}, ctx *queryContext) {
if array, ok := node.([]*toml.Tree); ok { if array, ok := node.([]*toml.Tree); ok {
for _, tree := range array { for _, tree := range array {
item := tree.Get(f.Name) item := tree.GetPath([]string{f.Name})
if item != nil { if item != nil {
ctx.lastPosition = tree.GetPosition(f.Name) ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
f.next.call(item, ctx) f.next.call(item, ctx)
} }
} }
} else if tree, ok := node.(*toml.Tree); ok { } else if tree, ok := node.(*toml.Tree); ok {
item := tree.Get(f.Name) item := tree.GetPath([]string{f.Name})
if item != nil { if item != nil {
ctx.lastPosition = tree.GetPosition(f.Name) ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
f.next.call(item, ctx) f.next.call(item, ctx)
} }
} }
@@ -129,8 +130,8 @@ func newMatchAnyFn() *matchAnyFn {
func (f *matchAnyFn) call(node interface{}, ctx *queryContext) { func (f *matchAnyFn) call(node interface{}, ctx *queryContext) {
if tree, ok := node.(*toml.Tree); ok { if tree, ok := node.(*toml.Tree); ok {
for _, k := range tree.Keys() { for _, k := range tree.Keys() {
v := tree.Get(k) v := tree.GetPath([]string{k})
ctx.lastPosition = tree.GetPosition(k) ctx.lastPosition = tree.GetPositionPath([]string{k})
f.next.call(v, ctx) f.next.call(v, ctx)
} }
} }
@@ -168,8 +169,8 @@ func (f *matchRecursiveFn) call(node interface{}, ctx *queryContext) {
var visit func(tree *toml.Tree) var visit func(tree *toml.Tree)
visit = func(tree *toml.Tree) { visit = func(tree *toml.Tree) {
for _, k := range tree.Keys() { for _, k := range tree.Keys() {
v := tree.Get(k) v := tree.GetPath([]string{k})
ctx.lastPosition = tree.GetPosition(k) ctx.lastPosition = tree.GetPositionPath([]string{k})
f.next.call(v, ctx) f.next.call(v, ctx)
switch node := v.(type) { switch node := v.(type) {
case *toml.Tree: case *toml.Tree:
@@ -207,9 +208,9 @@ func (f *matchFilterFn) call(node interface{}, ctx *queryContext) {
switch castNode := node.(type) { switch castNode := node.(type) {
case *toml.Tree: case *toml.Tree:
for _, k := range castNode.Keys() { for _, k := range castNode.Keys() {
v := castNode.Get(k) v := castNode.GetPath([]string{k})
if fn(v) { if fn(v) {
ctx.lastPosition = castNode.GetPosition(k) ctx.lastPosition = castNode.GetPositionPath([]string{k})
f.next.call(v, ctx) f.next.call(v, ctx)
} }
} }
+4 -4
View File
@@ -2,9 +2,9 @@ package query
import ( import (
"fmt" "fmt"
"github.com/pelletier/go-toml"
"strconv" "strconv"
"unicode"
"github.com/pelletier/go-toml"
) )
// Define tokens // Define tokens
@@ -92,11 +92,11 @@ func isSpace(r rune) bool {
} }
func isAlphanumeric(r rune) bool { func isAlphanumeric(r rune) bool {
return unicode.IsLetter(r) || r == '_' return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_'
} }
func isDigit(r rune) bool { func isDigit(r rune) bool {
return unicode.IsNumber(r) return '0' <= r && r <= '9'
} }
func isHexDigit(r rune) bool { func isHexDigit(r rune) bool {
+3 -6
View File
@@ -1,9 +1,6 @@
package toml package toml
import ( import "fmt"
"fmt"
"unicode"
)
// Define tokens // Define tokens
type tokenType int type tokenType int
@@ -112,7 +109,7 @@ func isSpace(r rune) bool {
} }
func isAlphanumeric(r rune) bool { func isAlphanumeric(r rune) bool {
return unicode.IsLetter(r) || r == '_' return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_'
} }
func isKeyChar(r rune) bool { func isKeyChar(r rune) bool {
@@ -127,7 +124,7 @@ func isKeyStartChar(r rune) bool {
} }
func isDigit(r rune) bool { func isDigit(r rune) bool {
return unicode.IsNumber(r) return '0' <= r && r <= '9'
} }
func isHexDigit(r rune) bool { func isHexDigit(r rune) bool {
+1 -1
View File
@@ -338,7 +338,7 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i
k := node.key k := node.key
v := t.values[k] v := t.values[k]
combinedKey := k combinedKey := quoteKeyIfNeeded(k)
if keyspace != "" { if keyspace != "" {
combinedKey = keyspace + "." + combinedKey combinedKey = keyspace + "." + combinedKey
} }