Fixing TOML ArrayTable parsing issues #1758

This commit is contained in:
Mike Farah 2025-11-22 14:49:49 +11:00
parent 306dc931a5
commit 39fbf01fa8
2 changed files with 57 additions and 23 deletions

View File

@ -267,6 +267,14 @@ func (dec *tomlDecoder) processTable(currentNode *toml.Node) (bool, error) {
fullPath := dec.getFullPath(currentNode.Child())
log.Debug("fullpath: %v", fullPath)
c := Context{}
c = c.SingleChildContext(dec.rootMap)
fullPath, err := getPathToUse(fullPath, dec, c)
if err != nil {
return false, err
}
tableNodeValue := &CandidateNode{
Kind: MappingNode,
Tag: "!!map",
@ -275,7 +283,6 @@ func (dec *tomlDecoder) processTable(currentNode *toml.Node) (bool, error) {
var tableValue *toml.Node
runAgainstCurrentExp := false
var err error
hasValue := dec.parser.NextExpression()
// check to see if there is any table data
if hasValue {
@ -292,8 +299,6 @@ func (dec *tomlDecoder) processTable(currentNode *toml.Node) (bool, error) {
}
}
c := Context{}
c = c.SingleChildContext(dec.rootMap)
err = dec.d.DeeplyAssign(c, fullPath, tableNodeValue)
if err != nil {
return false, err
@ -324,35 +329,17 @@ func (dec *tomlDecoder) arrayAppend(context Context, path []interface{}, rhsNode
}
func (dec *tomlDecoder) processArrayTable(currentNode *toml.Node) (bool, error) {
log.Debug("c")
log.Debug("Enter processArrayTable")
fullPath := dec.getFullPath(currentNode.Child())
log.Debug("Fullpath: %v", fullPath)
c := Context{}
c = c.SingleChildContext(dec.rootMap)
pathToCheck := fullPath
if len(fullPath) >= 1 {
pathToCheck = fullPath[:len(fullPath)-1]
}
// if fullPath points to an array of maps rather than a map
// then it should set this element into the _last_ element of that array.
// Because TOML. So we'll inject the last index into the path.
readOp := createTraversalTree(pathToCheck, traversePreferences{DontAutoCreate: true}, false)
resultContext, err := dec.d.GetMatchingNodes(c, readOp)
fullPath, err := getPathToUse(fullPath, dec, c)
if err != nil {
return false, err
}
if resultContext.MatchingNodes.Len() >= 1 {
match := resultContext.MatchingNodes.Front().Value.(*CandidateNode)
// path refers to an array, we need to add this to the last element in the array
if match.Kind == SequenceNode {
fullPath = append(pathToCheck, len(match.Content)-1, fullPath[len(fullPath)-1])
log.Debugf("Adding to end of %v array, using path: %v", pathToCheck, fullPath)
}
}
// need to use the array append exp to add another entry to
// this array: fullpath += [ thing ]
@ -382,3 +369,29 @@ func (dec *tomlDecoder) processArrayTable(currentNode *toml.Node) (bool, error)
return runAgainstCurrentExp, err
}
// if fullPath points to an array of maps rather than a map
// then it should set this element into the _last_ element of that array.
// Because TOML. So we'll inject the last index into the path.
func getPathToUse(fullPath []interface{}, dec *tomlDecoder, c Context) ([]interface{}, error) {
pathToCheck := fullPath
if len(fullPath) >= 1 {
pathToCheck = fullPath[:len(fullPath)-1]
}
readOp := createTraversalTree(pathToCheck, traversePreferences{DontAutoCreate: true}, false)
resultContext, err := dec.d.GetMatchingNodes(c, readOp)
if err != nil {
return nil, err
}
if resultContext.MatchingNodes.Len() >= 1 {
match := resultContext.MatchingNodes.Front().Value.(*CandidateNode)
// path refers to an array, we need to add this to the last element in the array
if match.Kind == SequenceNode {
fullPath = append(pathToCheck, len(match.Content)-1, fullPath[len(fullPath)-1])
log.Debugf("Adding to end of %v array, using path: %v", pathToCheck, fullPath)
}
}
return fullPath, err
}

View File

@ -95,6 +95,20 @@ var emptyArrayTableThenTableExpected = `fruits:
animals: {}
`
var arrayTableThenArray = `
[[rootA.kidB]]
cat = "meow"
[rootA.kidB.kidC]
dog = "bark"`
var arrayTableThenArrayExpected = `rootA:
kidB:
- cat: meow
kidC:
dog: bark
`
var sampleArrayTable = `
[owner.contact]
name = "Tom Preston-Werner"
@ -341,6 +355,13 @@ var tomlScenarios = []formatScenario{
expected: emptyArrayTableThenTableExpected,
scenarioType: "decode",
},
{
skipDoc: true,
description: "Parse: Array of Array Table; then table",
input: arrayTableThenArray,
expected: arrayTableThenArrayExpected,
scenarioType: "decode",
},
{
description: "Parse: Empty Table",
input: emptyTable,