@@ -19,7 +19,9 @@ package interpolation
19
19
import (
20
20
"errors"
21
21
"fmt"
22
+ "github.com/compose-spec/compose-go/v2/arraytemplate"
22
23
"os"
24
+ "reflect"
23
25
24
26
"github.com/compose-spec/compose-go/v2/template"
25
27
"github.com/compose-spec/compose-go/v2/tree"
@@ -32,7 +34,12 @@ type Options struct {
32
34
// TypeCastMapping maps key paths to functions to cast to a type
33
35
TypeCastMapping map [tree.Path ]Cast
34
36
// Substitution function to use
35
- Substitute func (string , template.Mapping ) (string , error )
37
+ Substitute func (string , LookupValue ) (* SubstitutionResult , error )
38
+ }
39
+
40
+ type SubstitutionResult struct {
41
+ String string
42
+ Array []string
36
43
}
37
44
38
45
// LookupValue is a function which maps from variable names to values.
@@ -53,7 +60,7 @@ func Interpolate(config map[string]interface{}, opts Options) (map[string]interf
53
60
opts .TypeCastMapping = make (map [tree.Path ]Cast )
54
61
}
55
62
if opts .Substitute == nil {
56
- opts .Substitute = template . Substitute
63
+ opts .Substitute = DefaultSubstitute
57
64
}
58
65
59
66
out := map [string ]interface {}{}
@@ -69,18 +76,30 @@ func Interpolate(config map[string]interface{}, opts Options) (map[string]interf
69
76
return out , nil
70
77
}
71
78
79
+ func DefaultSubstitute (t string , lookup LookupValue ) (* SubstitutionResult , error ) {
80
+ if (arraytemplate .ArraySubstitutionPattern ).MatchString (t ) {
81
+ arr , err := arraytemplate .Substitute (t , arraytemplate .Mapping (lookup ))
82
+ return & SubstitutionResult {Array : arr }, err
83
+ }
84
+ str , err := template .Substitute (t , template .Mapping (lookup ))
85
+ return & SubstitutionResult {String : str }, err
86
+ }
87
+
72
88
func recursiveInterpolate (value interface {}, path tree.Path , opts Options ) (interface {}, error ) {
73
89
switch value := value .(type ) {
74
90
case string :
75
- newValue , err := opts .Substitute (value , template . Mapping ( opts .LookupValue ) )
91
+ result , err := opts .Substitute (value , opts .LookupValue )
76
92
if err != nil {
77
93
return value , newPathError (path , err )
78
94
}
95
+ if result .Array != nil {
96
+ return result .Array , nil
97
+ }
79
98
caster , ok := opts .getCasterForPath (path )
80
99
if ! ok {
81
- return newValue , nil
100
+ return result . String , nil
82
101
}
83
- casted , err := caster (newValue )
102
+ casted , err := caster (result . String )
84
103
if err != nil {
85
104
return casted , newPathError (path , fmt .Errorf ("failed to cast to expected type: %w" , err ))
86
105
}
@@ -98,13 +117,19 @@ func recursiveInterpolate(value interface{}, path tree.Path, opts Options) (inte
98
117
return out , nil
99
118
100
119
case []interface {}:
101
- out := make ([]interface {}, len (value ))
102
- for i , elem := range value {
120
+ out := make ([]interface {}, 0 , len (value ))
121
+ for _ , elem := range value {
103
122
interpolatedElem , err := recursiveInterpolate (elem , path .Next (tree .PathMatchList ), opts )
104
123
if err != nil {
105
124
return nil , err
106
125
}
107
- out [i ] = interpolatedElem
126
+ if isStringSlice (interpolatedElem ) {
127
+ for _ , nestedElem := range interpolatedElem .([]string ) {
128
+ out = append (out , nestedElem )
129
+ }
130
+ } else {
131
+ out = append (out , interpolatedElem )
132
+ }
108
133
}
109
134
return out , nil
110
135
@@ -113,6 +138,20 @@ func recursiveInterpolate(value interface{}, path tree.Path, opts Options) (inte
113
138
}
114
139
}
115
140
141
+ func isStringSlice (value interface {}) bool {
142
+ if value == nil {
143
+ return false
144
+ }
145
+ t := reflect .TypeOf (value )
146
+ if t .Kind () != reflect .Slice {
147
+ return false
148
+ }
149
+ if t .Elem ().Kind () != reflect .String {
150
+ return false
151
+ }
152
+ return true
153
+ }
154
+
116
155
func newPathError (path tree.Path , err error ) error {
117
156
var ite * template.InvalidTemplateError
118
157
switch {
0 commit comments