Skip to content

Commit 8f3291d

Browse files
authored
fix: decode properties array bracket paths (#2693)
* fix: decode properties array bracket paths * test: add nested array bracket properties decode case --------- Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
1 parent 2861815 commit 8f3291d

2 files changed

Lines changed: 85 additions & 11 deletions

File tree

pkg/yqlib/decoder_properties.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ type propertiesDecoder struct {
1616
reader io.Reader
1717
finished bool
1818
d DataTreeNavigator
19+
prefs PropertiesPreferences
1920
}
2021

2122
func NewPropertiesDecoder() Decoder {
22-
return &propertiesDecoder{d: NewDataTreeNavigator(), finished: false}
23+
return &propertiesDecoder{d: NewDataTreeNavigator(), finished: false, prefs: ConfiguredPropertiesPreferences.Copy()}
2324
}
2425

2526
func (dec *propertiesDecoder) Init(reader io.Reader) error {
@@ -28,20 +29,56 @@ func (dec *propertiesDecoder) Init(reader io.Reader) error {
2829
return nil
2930
}
3031

31-
func parsePropKey(key string) []interface{} {
32+
func parsePropKey(key string, prefs PropertiesPreferences) []interface{} {
3233
pathStrArray := strings.Split(key, ".")
33-
path := make([]interface{}, len(pathStrArray))
34-
for i, pathStr := range pathStrArray {
35-
num, err := strconv.ParseInt(pathStr, 10, 32)
36-
if err == nil {
37-
path[i] = num
38-
} else {
39-
path[i] = pathStr
40-
}
34+
path := make([]interface{}, 0, len(pathStrArray))
35+
for _, pathStr := range pathStrArray {
36+
path = appendPropKeySegment(path, pathStr, prefs.UseArrayBrackets)
4137
}
4238
return path
4339
}
4440

41+
func appendPropKeySegment(path []interface{}, segment string, useArrayBrackets bool) []interface{} {
42+
if useArrayBrackets && strings.Contains(segment, "[") {
43+
bracketPath, ok := parsePropKeyArrayBracketSegment(segment)
44+
if ok {
45+
return append(path, bracketPath...)
46+
}
47+
}
48+
49+
num, err := strconv.ParseInt(segment, 10, 32)
50+
if err == nil {
51+
return append(path, num)
52+
}
53+
return append(path, segment)
54+
}
55+
56+
func parsePropKeyArrayBracketSegment(segment string) ([]interface{}, bool) {
57+
path := []interface{}{}
58+
bracketIndex := strings.Index(segment, "[")
59+
if bracketIndex > 0 {
60+
path = append(path, segment[:bracketIndex])
61+
}
62+
63+
remaining := segment[bracketIndex:]
64+
for remaining != "" {
65+
if !strings.HasPrefix(remaining, "[") {
66+
return nil, false
67+
}
68+
closingBracket := strings.Index(remaining, "]")
69+
if closingBracket < 0 {
70+
return nil, false
71+
}
72+
arrayIndex, err := strconv.ParseInt(remaining[1:closingBracket], 10, 32)
73+
if err != nil {
74+
return nil, false
75+
}
76+
path = append(path, arrayIndex)
77+
remaining = remaining[closingBracket+1:]
78+
}
79+
return path, true
80+
}
81+
4582
func (dec *propertiesDecoder) processComment(c string) string {
4683
if c == "" {
4784
return ""
@@ -75,7 +112,7 @@ func (dec *propertiesDecoder) applyPropertyComments(context Context, path []inte
75112

76113
func (dec *propertiesDecoder) applyProperty(context Context, properties *properties.Properties, key string) error {
77114
value, _ := properties.Get(key)
78-
path := parsePropKey(key)
115+
path := parsePropKey(key, dec.prefs)
79116

80117
propertyComments := properties.GetComments(key)
81118
if len(propertyComments) > 0 {

pkg/yqlib/properties_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,37 @@ var propertyScenarios = []formatScenario{
202202
expected: expectedDecodedYaml,
203203
scenarioType: "decode",
204204
},
205+
{
206+
skipDoc: true,
207+
description: "Decode properties with array brackets",
208+
input: `user.credentials[0].username=user1
209+
user.credentials[0].password=$2b$08$...
210+
user.credentials[1].username=user2
211+
user.credentials[1].password=$2b$08$...
212+
user.credentials[2].username=user3
213+
user.credentials[2].password=$2b$10$...`,
214+
expected: `user:
215+
credentials:
216+
- username: user1
217+
password: $2b$08$...
218+
- username: user2
219+
password: $2b$08$...
220+
- username: user3
221+
password: $2b$10$...
222+
`,
223+
scenarioType: "decode-array-brackets",
224+
},
225+
{
226+
skipDoc: true,
227+
description: "Decode properties with nested array brackets",
228+
input: `user.clowns[0][1] = "cool"`,
229+
expected: `user:
230+
clowns:
231+
- - null
232+
- '"cool"'
233+
`,
234+
scenarioType: "decode-array-brackets",
235+
},
205236

206237
{
207238
skipDoc: true,
@@ -442,6 +473,12 @@ func TestPropertyScenarios(t *testing.T) {
442473
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewPropertiesEncoder(ConfiguredPropertiesPreferences)), s.description)
443474
case "decode":
444475
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewPropertiesDecoder(), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
476+
case "decode-array-brackets":
477+
previousPreferences := ConfiguredPropertiesPreferences.Copy()
478+
ConfiguredPropertiesPreferences.UseArrayBrackets = true
479+
actual := mustProcessFormatScenario(s, NewPropertiesDecoder(), NewYamlEncoder(ConfiguredYamlPreferences))
480+
ConfiguredPropertiesPreferences = previousPreferences
481+
test.AssertResultWithContext(t, s.expected, actual, s.description)
445482
case "encode-wrapped":
446483
prefs := ConfiguredPropertiesPreferences.Copy()
447484
prefs.UnwrapScalar = false

0 commit comments

Comments
 (0)