github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/config/interpolation/interpolation.go (about)

     1  package interpolation
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/SAP/jenkins-library/pkg/log"
     9  )
    10  
    11  const (
    12  	maxLookupDepth = 10
    13  )
    14  
    15  var (
    16  	lookupRegex   *regexp.Regexp = regexp.MustCompile(`\$\((?P<property>[a-zA-Z0-9\.]*)\)`)
    17  	captureGroups                = setupCaptureGroups(lookupRegex.SubexpNames())
    18  )
    19  
    20  // ResolveMap interpolates every string value of a map and tries to lookup references to other properties of that map
    21  func ResolveMap(config map[string]interface{}) bool {
    22  	for key, value := range config {
    23  		if str, ok := value.(string); ok {
    24  			resolvedStr, ok := ResolveString(str, config)
    25  			if !ok {
    26  				return false
    27  			}
    28  			config[key] = resolvedStr
    29  		}
    30  	}
    31  	return true
    32  }
    33  
    34  func resolveString(str string, lookupMap map[string]interface{}, n int) (string, bool) {
    35  	matches := lookupRegex.FindAllStringSubmatch(str, -1)
    36  	if len(matches) == 0 {
    37  		return str, true
    38  	}
    39  	if n == maxLookupDepth {
    40  		log.Entry().Errorf("Property could not be resolved with a depth of %d. '%s' is still left to resolve", n, str)
    41  		return "", false
    42  	}
    43  	for _, match := range matches {
    44  		property := match[captureGroups["property"]]
    45  		if propVal, ok := lookupMap[property]; ok {
    46  			str = strings.ReplaceAll(str, fmt.Sprintf("$(%s)", property), propVal.(string))
    47  		} else {
    48  			// value not found
    49  			log.Entry().Debugf("Can't interploate '%s'. Missing property '%s'", str, property)
    50  			return "", false
    51  		}
    52  	}
    53  	return resolveString(str, lookupMap, n+1)
    54  }
    55  
    56  // ResolveString takes a string and replaces all references inside of it with values from the given lookupMap.
    57  // This is being done recursively until the maxLookupDepth is reached.
    58  func ResolveString(str string, lookupMap map[string]interface{}) (string, bool) {
    59  	return resolveString(str, lookupMap, 0)
    60  }
    61  
    62  func setupCaptureGroups(captureGroupsList []string) map[string]int {
    63  	groups := make(map[string]int, len(captureGroupsList))
    64  	for i, captureGroupName := range captureGroupsList {
    65  		if i == 0 {
    66  			continue
    67  		}
    68  		groups[captureGroupName] = i
    69  	}
    70  	return groups
    71  }