@@ -19,7 +19,9 @@ package interpolation
1919import (
2020 "errors"
2121 "fmt"
22+ "github.com/compose-spec/compose-go/v2/arraytemplate"
2223 "os"
24+ "reflect"
2325
2426 "github.com/compose-spec/compose-go/v2/template"
2527 "github.com/compose-spec/compose-go/v2/tree"
@@ -32,7 +34,12 @@ type Options struct {
3234 // TypeCastMapping maps key paths to functions to cast to a type
3335 TypeCastMapping map [tree.Path ]Cast
3436 // 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
3643}
3744
3845// 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
5360 opts .TypeCastMapping = make (map [tree.Path ]Cast )
5461 }
5562 if opts .Substitute == nil {
56- opts .Substitute = template . Substitute
63+ opts .Substitute = DefaultSubstitute
5764 }
5865
5966 out := map [string ]interface {}{}
@@ -69,18 +76,30 @@ func Interpolate(config map[string]interface{}, opts Options) (map[string]interf
6976 return out , nil
7077}
7178
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+
7288func recursiveInterpolate (value interface {}, path tree.Path , opts Options ) (interface {}, error ) {
7389 switch value := value .(type ) {
7490 case string :
75- newValue , err := opts .Substitute (value , template . Mapping ( opts .LookupValue ) )
91+ result , err := opts .Substitute (value , opts .LookupValue )
7692 if err != nil {
7793 return value , newPathError (path , err )
7894 }
95+ if result .Array != nil {
96+ return result .Array , nil
97+ }
7998 caster , ok := opts .getCasterForPath (path )
8099 if ! ok {
81- return newValue , nil
100+ return result . String , nil
82101 }
83- casted , err := caster (newValue )
102+ casted , err := caster (result . String )
84103 if err != nil {
85104 return casted , newPathError (path , fmt .Errorf ("failed to cast to expected type: %w" , err ))
86105 }
@@ -98,13 +117,19 @@ func recursiveInterpolate(value interface{}, path tree.Path, opts Options) (inte
98117 return out , nil
99118
100119 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 {
103122 interpolatedElem , err := recursiveInterpolate (elem , path .Next (tree .PathMatchList ), opts )
104123 if err != nil {
105124 return nil , err
106125 }
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+ }
108133 }
109134 return out , nil
110135
@@ -113,6 +138,20 @@ func recursiveInterpolate(value interface{}, path tree.Path, opts Options) (inte
113138 }
114139}
115140
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+
116155func newPathError (path tree.Path , err error ) error {
117156 var ite * template.InvalidTemplateError
118157 switch {
0 commit comments