json: magic flatten step to make things more HCL-like
This commit is contained in:
parent
0704c168e1
commit
60534579be
@ -132,6 +132,8 @@ func TestDecode_interface(t *testing.T) {
|
||||
"baz": []map[string]interface{}{
|
||||
map[string]interface{}{"key": 7},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"bar": []map[string]interface{}{
|
||||
map[string]interface{}{"key": 12},
|
||||
},
|
||||
@ -447,8 +449,9 @@ func TestDecode_structureMap(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
var actual rawConfig
|
||||
t.Logf("Testing: %s", f)
|
||||
|
||||
var actual rawConfig
|
||||
err := Decode(&actual, testReadFile(t, f))
|
||||
if err != nil {
|
||||
t.Fatalf("Input: %s\n\nerr: %s", f, err)
|
||||
|
@ -29,6 +29,13 @@ func Parse(v string) (*ast.File, error) {
|
||||
return nil, lex.err
|
||||
}
|
||||
|
||||
// If we have a result, flatten it. This is an operation we take on
|
||||
// to make our AST look more like traditional HCL. This makes parsing
|
||||
// it a lot easier later.
|
||||
if jsonResult != nil {
|
||||
flattenObjects(jsonResult)
|
||||
}
|
||||
|
||||
// Build up the errors
|
||||
var err error
|
||||
if len(jsonErrors) > 0 {
|
||||
@ -38,3 +45,72 @@ func Parse(v string) (*ast.File, error) {
|
||||
|
||||
return jsonResult, err
|
||||
}
|
||||
|
||||
// flattenObjects takes an AST node, walks it, and flattens
|
||||
func flattenObjects(node ast.Node) {
|
||||
ast.Walk(jsonResult, func(n ast.Node) bool {
|
||||
// We only care about lists, because this is what we modify
|
||||
list, ok := n.(*ast.ObjectList)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// Rebuild the item list
|
||||
items := make([]*ast.ObjectItem, 0, len(list.Items))
|
||||
frontier := make([]*ast.ObjectItem, len(list.Items))
|
||||
copy(frontier, list.Items)
|
||||
for len(frontier) > 0 {
|
||||
// Pop the current item
|
||||
n := len(frontier)
|
||||
item := frontier[n-1]
|
||||
frontier = frontier[:n-1]
|
||||
|
||||
// We only care if the value of this item is an object
|
||||
ot, ok := item.Val.(*ast.ObjectType)
|
||||
if !ok {
|
||||
items = append(items, item)
|
||||
continue
|
||||
}
|
||||
|
||||
// All the elements of this object must also be objects!
|
||||
match := true
|
||||
for _, item := range ot.List.Items {
|
||||
if _, ok := item.Val.(*ast.ObjectType); !ok {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
items = append(items, item)
|
||||
continue
|
||||
}
|
||||
|
||||
// Great! We have a match go through all the items and flatten
|
||||
for _, subitem := range ot.List.Items {
|
||||
// Copy the new key
|
||||
keys := make([]*ast.ObjectKey, len(item.Keys)+len(subitem.Keys))
|
||||
copy(keys, item.Keys)
|
||||
copy(keys[len(item.Keys):], subitem.Keys)
|
||||
|
||||
// Add it to the frontier so that we can recurse
|
||||
frontier = append(frontier, &ast.ObjectItem{
|
||||
Keys: keys,
|
||||
Assign: item.Assign,
|
||||
Val: subitem.Val,
|
||||
LeadComment: item.LeadComment,
|
||||
LineComment: item.LineComment,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the list since the frontier model runs things backwards
|
||||
for i := len(items)/2 - 1; i >= 0; i-- {
|
||||
opp := len(items) - 1 - i
|
||||
items[i], items[opp] = items[opp], items[i]
|
||||
}
|
||||
|
||||
// Done! Set the original items
|
||||
list.Items = items
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user