@@ -7,18 +7,30 @@ import (
77 "fmt"
88 "regexp"
99 "strings"
10+
11+ "github.com/docker/cli/internal/lazyregexp"
1012)
1113
1214const (
1315 delimiter = "\\ $"
1416 subst = "[_a-z][_a-z0-9]*(?::?[-?][^}]*)?"
1517)
1618
17- var defaultPattern = regexp . MustCompile (fmt .Sprintf (
19+ var defaultPattern = lazyregexp . New (fmt .Sprintf (
1820 "%s(?i:(?P<escaped>%s)|(?P<named>%s)|{(?P<braced>%s)}|(?P<invalid>))" ,
1921 delimiter , delimiter , subst , subst ,
2022))
2123
24+ // regexper is an internal interface to allow passing a [lazyregexp.Regexp]
25+ // in places where a custom ("regular") [regexp.Regexp] is accepted. It defines
26+ // only the methods we currently use.
27+ type regexper interface {
28+ FindAllStringSubmatch (s string , n int ) [][]string
29+ FindStringSubmatch (s string ) []string
30+ ReplaceAllStringFunc (src string , repl func (string ) string ) string
31+ SubexpNames () []string
32+ }
33+
2234// DefaultSubstituteFuncs contains the default SubstituteFunc used by the docker cli
2335var DefaultSubstituteFuncs = []SubstituteFunc {
2436 softDefault ,
@@ -51,10 +63,16 @@ type SubstituteFunc func(string, Mapping) (string, bool, error)
5163// SubstituteWith substitutes variables in the string with their values.
5264// It accepts additional substitute function.
5365func SubstituteWith (template string , mapping Mapping , pattern * regexp.Regexp , subsFuncs ... SubstituteFunc ) (string , error ) {
66+ return substituteWith (template , mapping , pattern , subsFuncs ... )
67+ }
68+
69+ // SubstituteWith substitutes variables in the string with their values.
70+ // It accepts additional substitute function.
71+ func substituteWith (template string , mapping Mapping , pattern regexper , subsFuncs ... SubstituteFunc ) (string , error ) {
5472 var err error
5573 result := pattern .ReplaceAllStringFunc (template , func (substring string ) string {
5674 matches := pattern .FindStringSubmatch (substring )
57- groups := matchGroups (matches , pattern )
75+ groups := matchGroups (matches , defaultPattern )
5876 if escaped := groups ["escaped" ]; escaped != "" {
5977 return escaped
6078 }
@@ -93,38 +111,42 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
93111
94112// Substitute variables in the string with their values
95113func Substitute (template string , mapping Mapping ) (string , error ) {
96- return SubstituteWith (template , mapping , defaultPattern , DefaultSubstituteFuncs ... )
114+ return substituteWith (template , mapping , defaultPattern , DefaultSubstituteFuncs ... )
97115}
98116
99117// ExtractVariables returns a map of all the variables defined in the specified
100118// composefile (dict representation) and their default value if any.
101119func ExtractVariables (configDict map [string ]any , pattern * regexp.Regexp ) map [string ]string {
120+ return extractVariables (configDict , pattern )
121+ }
122+
123+ func extractVariables (configDict map [string ]any , pattern regexper ) map [string ]string {
102124 if pattern == nil {
103125 pattern = defaultPattern
104126 }
105127 return recurseExtract (configDict , pattern )
106128}
107129
108- func recurseExtract (value any , pattern * regexp. Regexp ) map [string ]string {
130+ func recurseExtract (value any , pattern regexper ) map [string ]string {
109131 m := map [string ]string {}
110132
111- switch value := value .(type ) {
133+ switch val := value .(type ) {
112134 case string :
113- if values , is := extractVariable (value , pattern ); is {
135+ if values , is := extractVariable (val , pattern ); is {
114136 for _ , v := range values {
115137 m [v .name ] = v .value
116138 }
117139 }
118140 case map [string ]any :
119- for _ , elem := range value {
141+ for _ , elem := range val {
120142 submap := recurseExtract (elem , pattern )
121- for key , value := range submap {
122- m [key ] = value
143+ for k , v := range submap {
144+ m [k ] = v
123145 }
124146 }
125147
126148 case []any :
127- for _ , elem := range value {
149+ for _ , elem := range val {
128150 if values , is := extractVariable (elem , pattern ); is {
129151 for _ , v := range values {
130152 m [v .name ] = v .value
@@ -141,7 +163,7 @@ type extractedValue struct {
141163 value string
142164}
143165
144- func extractVariable (value any , pattern * regexp. Regexp ) ([]extractedValue , bool ) {
166+ func extractVariable (value any , pattern regexper ) ([]extractedValue , bool ) {
145167 sValue , ok := value .(string )
146168 if ! ok {
147169 return []extractedValue {}, false
@@ -227,7 +249,7 @@ func withRequired(substitution string, mapping Mapping, sep string, valid func(s
227249 return value , true , nil
228250}
229251
230- func matchGroups (matches []string , pattern * regexp. Regexp ) map [string ]string {
252+ func matchGroups (matches []string , pattern regexper ) map [string ]string {
231253 groups := make (map [string ]string )
232254 for i , name := range pattern .SubexpNames ()[1 :] {
233255 groups [name ] = matches [i + 1 ]
0 commit comments