github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/command/expansion.go (about)

     1  package command
     2  
     3  import (
     4  	"io/ioutil"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  	yaml "gopkg.in/yaml.v2"
    10  )
    11  
    12  var (
    13  	expansionRegex = regexp.MustCompile(`\$\{.*?\}`)
    14  )
    15  
    16  // Wrapper for an expansions map, with some utility functions.
    17  type Expansions map[string]string
    18  
    19  // Return a new Expansions object with all of the specified expansions present.
    20  func NewExpansions(initMap map[string]string) *Expansions {
    21  	exp := Expansions(map[string]string{})
    22  	exp.Update(initMap)
    23  	return &exp
    24  }
    25  
    26  // Update all of the specified keys in the expansions to point to the specified
    27  // values.
    28  func (self *Expansions) Update(newItems map[string]string) {
    29  	for k, v := range newItems {
    30  		self.Put(k, v)
    31  	}
    32  }
    33  
    34  // Read a map of keys/values from the given file, and update the expansions
    35  // to include them (overwriting any duplicates with the new value).
    36  func (self *Expansions) UpdateFromYaml(filename string) error {
    37  	filedata, err := ioutil.ReadFile(filename)
    38  	if err != nil {
    39  		return err
    40  	}
    41  	newExpansions := make(map[string]string)
    42  	err = yaml.Unmarshal(filedata, newExpansions)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	self.Update(newExpansions)
    47  	return nil
    48  }
    49  
    50  // Set a single value in the expansions.
    51  func (self *Expansions) Put(expansion string, value string) {
    52  	(*self)[expansion] = value
    53  }
    54  
    55  // Get a single value from the expansions.
    56  // Return the value, or the empty string if the value is not present.
    57  func (self *Expansions) Get(expansion string) string {
    58  	if self.Exists(expansion) {
    59  		return (*self)[expansion]
    60  	}
    61  	return ""
    62  }
    63  
    64  // Check if a value is present in the expansions.
    65  func (self *Expansions) Exists(expansion string) bool {
    66  	_, ok := (*self)[expansion]
    67  	return ok
    68  }
    69  
    70  // Apply the expansions to a single string.
    71  // Return the expanded string, or an error if the input string is malformed.
    72  func (self *Expansions) ExpandString(toExpand string) (string, error) {
    73  	// replace all expandable parts of the string
    74  	malformedFound := false
    75  	expanded := string(expansionRegex.ReplaceAllFunc([]byte(toExpand),
    76  		func(matchByte []byte) []byte {
    77  
    78  			match := string(matchByte)
    79  			// trim off ${ and }
    80  			match = match[2 : len(match)-1]
    81  
    82  			// if there is a ${ within the match, then it is malformed (we
    83  			// caught an unmatched ${)
    84  			if strings.Contains(match, "${") {
    85  				malformedFound = true
    86  			}
    87  
    88  			// parse into the name and default value
    89  			var defaultVal string
    90  			if idx := strings.Index(match, "|"); idx != -1 {
    91  				defaultVal = match[idx+1:]
    92  				match = match[0:idx]
    93  			}
    94  
    95  			// return the specified expansion, if it is present.
    96  			if self.Exists(match) {
    97  				return []byte(self.Get(match))
    98  			}
    99  
   100  			return []byte(defaultVal)
   101  		}))
   102  
   103  	if malformedFound || strings.Contains(expanded, "${") {
   104  		return expanded, errors.Errorf("'%s' contains an unclosed expansion", expanded)
   105  	}
   106  
   107  	return expanded, nil
   108  }