Skip to content

Commit d18887a

Browse files
committed
Fix properties mapping utility function handling of overlapping keys
The Arduino project configuration fields have an odd usage of the properties.Map format. Dots may sometimes indicate nested keys, but in other cases they are merely a character in the key string. Previously, the utility function used to recursively map to a configurable depth was not correctly handling this situation due to using the same code both for the recursive mapping to subkeys as well as for the flat mapping at the final depth. The fix for the bug also makes the function more efficient by avoiding calling SubTree() when it's already known there is no subtree.
1 parent a4c8fdc commit d18887a

File tree

2 files changed

+37
-12
lines changed

2 files changed

+37
-12
lines changed

internal/project/general/general.go

+11-12
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,19 @@ In the event a full recursion of key levels is desired, set the levels argument
3232
func PropertiesToMap(flatProperties *properties.Map, levels int) map[string]interface{} {
3333
propertiesInterface := make(map[string]interface{})
3434

35-
var keys []string
3635
if levels != 1 {
37-
keys = flatProperties.FirstLevelKeys()
36+
for _, key := range flatProperties.FirstLevelKeys() {
37+
subTree := flatProperties.SubTree(key)
38+
if subTree.Size() > 0 {
39+
// This key contains a map.
40+
propertiesInterface[key] = PropertiesToMap(subTree, levels-1)
41+
} else {
42+
// This key contains a string, no more recursion is possible.
43+
propertiesInterface[key] = flatProperties.Get(key)
44+
}
45+
}
3846
} else {
39-
keys = flatProperties.Keys()
40-
}
41-
42-
for _, key := range keys {
43-
subTree := flatProperties.SubTree(key)
44-
if subTree.Size() > 0 {
45-
// This key contains a map.
46-
propertiesInterface[key] = PropertiesToMap(subTree, levels-1)
47-
} else {
48-
// This key contains a string, no more recursion is possible.
47+
for _, key := range flatProperties.Keys() {
4948
propertiesInterface[key] = flatProperties.Get(key)
5049
}
5150
}

internal/project/general/general_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ func TestPropertiesToMap(t *testing.T) {
3131
foo.bar=asdf
3232
foo.baz=zxcv
3333
bar.bat.bam=123
34+
qux.a=x
35+
qux.a.b=y
36+
fuz.a.b=y
37+
fuz.a=x
3438
`)
3539
propertiesInput, err := properties.LoadFromBytes(rawProperties)
3640
require.Nil(t, err)
@@ -41,6 +45,10 @@ func TestPropertiesToMap(t *testing.T) {
4145
"foo.bar": "asdf",
4246
"foo.baz": "zxcv",
4347
"bar.bat.bam": "123",
48+
"qux.a": "x",
49+
"qux.a.b": "y",
50+
"fuz.a.b": "y",
51+
"fuz.a": "x",
4452
}
4553

4654
assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToMap(propertiesInput, 1)))
@@ -55,6 +63,14 @@ func TestPropertiesToMap(t *testing.T) {
5563
"bar": map[string]interface{}{
5664
"bat.bam": "123",
5765
},
66+
"qux": map[string]interface{}{
67+
"a": "x",
68+
"a.b": "y",
69+
},
70+
"fuz": map[string]interface{}{
71+
"a.b": "y",
72+
"a": "x",
73+
},
5874
}
5975

6076
assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToMap(propertiesInput, 2)))
@@ -71,6 +87,16 @@ func TestPropertiesToMap(t *testing.T) {
7187
"bam": "123",
7288
},
7389
},
90+
"qux": map[string]interface{}{
91+
"a": map[string]interface{}{
92+
"b": "y", // It is impossible to represent the complete "properties" data structure recursed to this depth.
93+
},
94+
},
95+
"fuz": map[string]interface{}{
96+
"a": map[string]interface{}{
97+
"b": "y",
98+
},
99+
},
74100
}
75101

76102
assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToMap(propertiesInput, 3)))

0 commit comments

Comments
 (0)