github.com/archgrove/terraform@v0.9.5-0.20170502093151-adb789f0f8d2/config/interpolate_funcs.go (about)

     1  package config
     2  
     3  import (
     4  	"crypto/md5"
     5  	"crypto/sha1"
     6  	"crypto/sha256"
     7  	"encoding/base64"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"math"
    13  	"net"
    14  	"path/filepath"
    15  	"regexp"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  	"time"
    20  
    21  	"github.com/apparentlymart/go-cidr/cidr"
    22  	"github.com/hashicorp/go-uuid"
    23  	"github.com/hashicorp/hil"
    24  	"github.com/hashicorp/hil/ast"
    25  	"github.com/mitchellh/go-homedir"
    26  )
    27  
    28  // stringSliceToVariableValue converts a string slice into the value
    29  // required to be returned from interpolation functions which return
    30  // TypeList.
    31  func stringSliceToVariableValue(values []string) []ast.Variable {
    32  	output := make([]ast.Variable, len(values))
    33  	for index, value := range values {
    34  		output[index] = ast.Variable{
    35  			Type:  ast.TypeString,
    36  			Value: value,
    37  		}
    38  	}
    39  	return output
    40  }
    41  
    42  func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) {
    43  	output := make([]string, len(values))
    44  	for index, value := range values {
    45  		if value.Type != ast.TypeString {
    46  			return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String())
    47  		}
    48  		output[index] = value.Value.(string)
    49  	}
    50  	return output, nil
    51  }
    52  
    53  // Funcs is the mapping of built-in functions for configuration.
    54  func Funcs() map[string]ast.Function {
    55  	return map[string]ast.Function{
    56  		"basename":     interpolationFuncBasename(),
    57  		"base64decode": interpolationFuncBase64Decode(),
    58  		"base64encode": interpolationFuncBase64Encode(),
    59  		"base64sha256": interpolationFuncBase64Sha256(),
    60  		"ceil":         interpolationFuncCeil(),
    61  		"chomp":        interpolationFuncChomp(),
    62  		"cidrhost":     interpolationFuncCidrHost(),
    63  		"cidrnetmask":  interpolationFuncCidrNetmask(),
    64  		"cidrsubnet":   interpolationFuncCidrSubnet(),
    65  		"coalesce":     interpolationFuncCoalesce(),
    66  		"coalescelist": interpolationFuncCoalesceList(),
    67  		"compact":      interpolationFuncCompact(),
    68  		"concat":       interpolationFuncConcat(),
    69  		"dirname":      interpolationFuncDirname(),
    70  		"distinct":     interpolationFuncDistinct(),
    71  		"element":      interpolationFuncElement(),
    72  		"file":         interpolationFuncFile(),
    73  		"matchkeys":    interpolationFuncMatchKeys(),
    74  		"floor":        interpolationFuncFloor(),
    75  		"format":       interpolationFuncFormat(),
    76  		"formatlist":   interpolationFuncFormatList(),
    77  		"index":        interpolationFuncIndex(),
    78  		"join":         interpolationFuncJoin(),
    79  		"jsonencode":   interpolationFuncJSONEncode(),
    80  		"length":       interpolationFuncLength(),
    81  		"list":         interpolationFuncList(),
    82  		"lower":        interpolationFuncLower(),
    83  		"map":          interpolationFuncMap(),
    84  		"max":          interpolationFuncMax(),
    85  		"md5":          interpolationFuncMd5(),
    86  		"merge":        interpolationFuncMerge(),
    87  		"min":          interpolationFuncMin(),
    88  		"pathexpand":   interpolationFuncPathExpand(),
    89  		"uuid":         interpolationFuncUUID(),
    90  		"replace":      interpolationFuncReplace(),
    91  		"sha1":         interpolationFuncSha1(),
    92  		"sha256":       interpolationFuncSha256(),
    93  		"signum":       interpolationFuncSignum(),
    94  		"slice":        interpolationFuncSlice(),
    95  		"sort":         interpolationFuncSort(),
    96  		"split":        interpolationFuncSplit(),
    97  		"substr":       interpolationFuncSubstr(),
    98  		"timestamp":    interpolationFuncTimestamp(),
    99  		"title":        interpolationFuncTitle(),
   100  		"trimspace":    interpolationFuncTrimSpace(),
   101  		"upper":        interpolationFuncUpper(),
   102  		"zipmap":       interpolationFuncZipMap(),
   103  	}
   104  }
   105  
   106  // interpolationFuncList creates a list from the parameters passed
   107  // to it.
   108  func interpolationFuncList() ast.Function {
   109  	return ast.Function{
   110  		ArgTypes:     []ast.Type{},
   111  		ReturnType:   ast.TypeList,
   112  		Variadic:     true,
   113  		VariadicType: ast.TypeAny,
   114  		Callback: func(args []interface{}) (interface{}, error) {
   115  			var outputList []ast.Variable
   116  
   117  			for i, val := range args {
   118  				switch v := val.(type) {
   119  				case string:
   120  					outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v})
   121  				case []ast.Variable:
   122  					outputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v})
   123  				case map[string]ast.Variable:
   124  					outputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v})
   125  				default:
   126  					return nil, fmt.Errorf("unexpected type %T for argument %d in list", v, i)
   127  				}
   128  			}
   129  
   130  			// we don't support heterogeneous types, so make sure all types match the first
   131  			if len(outputList) > 0 {
   132  				firstType := outputList[0].Type
   133  				for i, v := range outputList[1:] {
   134  					if v.Type != firstType {
   135  						return nil, fmt.Errorf("unexpected type %s for argument %d in list", v.Type, i+1)
   136  					}
   137  				}
   138  			}
   139  
   140  			return outputList, nil
   141  		},
   142  	}
   143  }
   144  
   145  // interpolationFuncMap creates a map from the parameters passed
   146  // to it.
   147  func interpolationFuncMap() ast.Function {
   148  	return ast.Function{
   149  		ArgTypes:     []ast.Type{},
   150  		ReturnType:   ast.TypeMap,
   151  		Variadic:     true,
   152  		VariadicType: ast.TypeAny,
   153  		Callback: func(args []interface{}) (interface{}, error) {
   154  			outputMap := make(map[string]ast.Variable)
   155  
   156  			if len(args)%2 != 0 {
   157  				return nil, fmt.Errorf("requires an even number of arguments, got %d", len(args))
   158  			}
   159  
   160  			var firstType *ast.Type
   161  			for i := 0; i < len(args); i += 2 {
   162  				key, ok := args[i].(string)
   163  				if !ok {
   164  					return nil, fmt.Errorf("argument %d represents a key, so it must be a string", i+1)
   165  				}
   166  				val := args[i+1]
   167  				variable, err := hil.InterfaceToVariable(val)
   168  				if err != nil {
   169  					return nil, err
   170  				}
   171  				// Enforce map type homogeneity
   172  				if firstType == nil {
   173  					firstType = &variable.Type
   174  				} else if variable.Type != *firstType {
   175  					return nil, fmt.Errorf("all map values must have the same type, got %s then %s", firstType.Printable(), variable.Type.Printable())
   176  				}
   177  				// Check for duplicate keys
   178  				if _, ok := outputMap[key]; ok {
   179  					return nil, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key)
   180  				}
   181  				outputMap[key] = variable
   182  			}
   183  
   184  			return outputMap, nil
   185  		},
   186  	}
   187  }
   188  
   189  // interpolationFuncCompact strips a list of multi-variable values
   190  // (e.g. as returned by "split") of any empty strings.
   191  func interpolationFuncCompact() ast.Function {
   192  	return ast.Function{
   193  		ArgTypes:   []ast.Type{ast.TypeList},
   194  		ReturnType: ast.TypeList,
   195  		Variadic:   false,
   196  		Callback: func(args []interface{}) (interface{}, error) {
   197  			inputList := args[0].([]ast.Variable)
   198  
   199  			var outputList []string
   200  			for _, val := range inputList {
   201  				strVal, ok := val.Value.(string)
   202  				if !ok {
   203  					return nil, fmt.Errorf(
   204  						"compact() may only be used with flat lists, this list contains elements of %s",
   205  						val.Type.Printable())
   206  				}
   207  				if strVal == "" {
   208  					continue
   209  				}
   210  
   211  				outputList = append(outputList, strVal)
   212  			}
   213  			return stringSliceToVariableValue(outputList), nil
   214  		},
   215  	}
   216  }
   217  
   218  // interpolationFuncCidrHost implements the "cidrhost" function that
   219  // fills in the host part of a CIDR range address to create a single
   220  // host address
   221  func interpolationFuncCidrHost() ast.Function {
   222  	return ast.Function{
   223  		ArgTypes: []ast.Type{
   224  			ast.TypeString, // starting CIDR mask
   225  			ast.TypeInt,    // host number to insert
   226  		},
   227  		ReturnType: ast.TypeString,
   228  		Variadic:   false,
   229  		Callback: func(args []interface{}) (interface{}, error) {
   230  			hostNum := args[1].(int)
   231  			_, network, err := net.ParseCIDR(args[0].(string))
   232  			if err != nil {
   233  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   234  			}
   235  
   236  			ip, err := cidr.Host(network, hostNum)
   237  			if err != nil {
   238  				return nil, err
   239  			}
   240  
   241  			return ip.String(), nil
   242  		},
   243  	}
   244  }
   245  
   246  // interpolationFuncCidrNetmask implements the "cidrnetmask" function
   247  // that returns the subnet mask in IP address notation.
   248  func interpolationFuncCidrNetmask() ast.Function {
   249  	return ast.Function{
   250  		ArgTypes: []ast.Type{
   251  			ast.TypeString, // CIDR mask
   252  		},
   253  		ReturnType: ast.TypeString,
   254  		Variadic:   false,
   255  		Callback: func(args []interface{}) (interface{}, error) {
   256  			_, network, err := net.ParseCIDR(args[0].(string))
   257  			if err != nil {
   258  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   259  			}
   260  
   261  			return net.IP(network.Mask).String(), nil
   262  		},
   263  	}
   264  }
   265  
   266  // interpolationFuncCidrSubnet implements the "cidrsubnet" function that
   267  // adds an additional subnet of the given length onto an existing
   268  // IP block expressed in CIDR notation.
   269  func interpolationFuncCidrSubnet() ast.Function {
   270  	return ast.Function{
   271  		ArgTypes: []ast.Type{
   272  			ast.TypeString, // starting CIDR mask
   273  			ast.TypeInt,    // number of bits to extend the prefix
   274  			ast.TypeInt,    // network number to append to the prefix
   275  		},
   276  		ReturnType: ast.TypeString,
   277  		Variadic:   false,
   278  		Callback: func(args []interface{}) (interface{}, error) {
   279  			extraBits := args[1].(int)
   280  			subnetNum := args[2].(int)
   281  			_, network, err := net.ParseCIDR(args[0].(string))
   282  			if err != nil {
   283  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   284  			}
   285  
   286  			// For portability with 32-bit systems where the subnet number
   287  			// will be a 32-bit int, we only allow extension of 32 bits in
   288  			// one call even if we're running on a 64-bit machine.
   289  			// (Of course, this is significant only for IPv6.)
   290  			if extraBits > 32 {
   291  				return nil, fmt.Errorf("may not extend prefix by more than 32 bits")
   292  			}
   293  
   294  			newNetwork, err := cidr.Subnet(network, extraBits, subnetNum)
   295  			if err != nil {
   296  				return nil, err
   297  			}
   298  
   299  			return newNetwork.String(), nil
   300  		},
   301  	}
   302  }
   303  
   304  // interpolationFuncCoalesce implements the "coalesce" function that
   305  // returns the first non null / empty string from the provided input
   306  func interpolationFuncCoalesce() ast.Function {
   307  	return ast.Function{
   308  		ArgTypes:     []ast.Type{ast.TypeString},
   309  		ReturnType:   ast.TypeString,
   310  		Variadic:     true,
   311  		VariadicType: ast.TypeString,
   312  		Callback: func(args []interface{}) (interface{}, error) {
   313  			if len(args) < 2 {
   314  				return nil, fmt.Errorf("must provide at least two arguments")
   315  			}
   316  			for _, arg := range args {
   317  				argument := arg.(string)
   318  
   319  				if argument != "" {
   320  					return argument, nil
   321  				}
   322  			}
   323  			return "", nil
   324  		},
   325  	}
   326  }
   327  
   328  // interpolationFuncCoalesceList implements the "coalescelist" function that
   329  // returns the first non empty list from the provided input
   330  func interpolationFuncCoalesceList() ast.Function {
   331  	return ast.Function{
   332  		ArgTypes:     []ast.Type{ast.TypeList},
   333  		ReturnType:   ast.TypeList,
   334  		Variadic:     true,
   335  		VariadicType: ast.TypeList,
   336  		Callback: func(args []interface{}) (interface{}, error) {
   337  			if len(args) < 2 {
   338  				return nil, fmt.Errorf("must provide at least two arguments")
   339  			}
   340  			for _, arg := range args {
   341  				argument := arg.([]ast.Variable)
   342  
   343  				if len(argument) > 0 {
   344  					return argument, nil
   345  				}
   346  			}
   347  			return make([]ast.Variable, 0), nil
   348  		},
   349  	}
   350  }
   351  
   352  // interpolationFuncConcat implements the "concat" function that concatenates
   353  // multiple lists.
   354  func interpolationFuncConcat() ast.Function {
   355  	return ast.Function{
   356  		ArgTypes:     []ast.Type{ast.TypeList},
   357  		ReturnType:   ast.TypeList,
   358  		Variadic:     true,
   359  		VariadicType: ast.TypeList,
   360  		Callback: func(args []interface{}) (interface{}, error) {
   361  			var outputList []ast.Variable
   362  
   363  			for _, arg := range args {
   364  				for _, v := range arg.([]ast.Variable) {
   365  					switch v.Type {
   366  					case ast.TypeString:
   367  						outputList = append(outputList, v)
   368  					case ast.TypeList:
   369  						outputList = append(outputList, v)
   370  					case ast.TypeMap:
   371  						outputList = append(outputList, v)
   372  					default:
   373  						return nil, fmt.Errorf("concat() does not support lists of %s", v.Type.Printable())
   374  					}
   375  				}
   376  			}
   377  
   378  			// we don't support heterogeneous types, so make sure all types match the first
   379  			if len(outputList) > 0 {
   380  				firstType := outputList[0].Type
   381  				for _, v := range outputList[1:] {
   382  					if v.Type != firstType {
   383  						return nil, fmt.Errorf("unexpected %s in list of %s", v.Type.Printable(), firstType.Printable())
   384  					}
   385  				}
   386  			}
   387  
   388  			return outputList, nil
   389  		},
   390  	}
   391  }
   392  
   393  // interpolationFuncFile implements the "file" function that allows
   394  // loading contents from a file.
   395  func interpolationFuncFile() ast.Function {
   396  	return ast.Function{
   397  		ArgTypes:   []ast.Type{ast.TypeString},
   398  		ReturnType: ast.TypeString,
   399  		Callback: func(args []interface{}) (interface{}, error) {
   400  			path, err := homedir.Expand(args[0].(string))
   401  			if err != nil {
   402  				return "", err
   403  			}
   404  			data, err := ioutil.ReadFile(path)
   405  			if err != nil {
   406  				return "", err
   407  			}
   408  
   409  			return string(data), nil
   410  		},
   411  	}
   412  }
   413  
   414  // interpolationFuncFormat implements the "format" function that does
   415  // string formatting.
   416  func interpolationFuncFormat() ast.Function {
   417  	return ast.Function{
   418  		ArgTypes:     []ast.Type{ast.TypeString},
   419  		Variadic:     true,
   420  		VariadicType: ast.TypeAny,
   421  		ReturnType:   ast.TypeString,
   422  		Callback: func(args []interface{}) (interface{}, error) {
   423  			format := args[0].(string)
   424  			return fmt.Sprintf(format, args[1:]...), nil
   425  		},
   426  	}
   427  }
   428  
   429  // interpolationFuncMax returns the maximum of the numeric arguments
   430  func interpolationFuncMax() ast.Function {
   431  	return ast.Function{
   432  		ArgTypes:     []ast.Type{ast.TypeFloat},
   433  		ReturnType:   ast.TypeFloat,
   434  		Variadic:     true,
   435  		VariadicType: ast.TypeFloat,
   436  		Callback: func(args []interface{}) (interface{}, error) {
   437  			max := args[0].(float64)
   438  
   439  			for i := 1; i < len(args); i++ {
   440  				max = math.Max(max, args[i].(float64))
   441  			}
   442  
   443  			return max, nil
   444  		},
   445  	}
   446  }
   447  
   448  // interpolationFuncMin returns the minimum of the numeric arguments
   449  func interpolationFuncMin() ast.Function {
   450  	return ast.Function{
   451  		ArgTypes:     []ast.Type{ast.TypeFloat},
   452  		ReturnType:   ast.TypeFloat,
   453  		Variadic:     true,
   454  		VariadicType: ast.TypeFloat,
   455  		Callback: func(args []interface{}) (interface{}, error) {
   456  			min := args[0].(float64)
   457  
   458  			for i := 1; i < len(args); i++ {
   459  				min = math.Min(min, args[i].(float64))
   460  			}
   461  
   462  			return min, nil
   463  		},
   464  	}
   465  }
   466  
   467  // interpolationFuncPathExpand will expand any `~`'s found with the full file path
   468  func interpolationFuncPathExpand() ast.Function {
   469  	return ast.Function{
   470  		ArgTypes:   []ast.Type{ast.TypeString},
   471  		ReturnType: ast.TypeString,
   472  		Callback: func(args []interface{}) (interface{}, error) {
   473  			return homedir.Expand(args[0].(string))
   474  		},
   475  	}
   476  }
   477  
   478  // interpolationFuncCeil returns the the least integer value greater than or equal to the argument
   479  func interpolationFuncCeil() ast.Function {
   480  	return ast.Function{
   481  		ArgTypes:   []ast.Type{ast.TypeFloat},
   482  		ReturnType: ast.TypeInt,
   483  		Callback: func(args []interface{}) (interface{}, error) {
   484  			return int(math.Ceil(args[0].(float64))), nil
   485  		},
   486  	}
   487  }
   488  
   489  // interpolationFuncChomp removes trailing newlines from the given string
   490  func interpolationFuncChomp() ast.Function {
   491  	newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`)
   492  	return ast.Function{
   493  		ArgTypes:   []ast.Type{ast.TypeString},
   494  		ReturnType: ast.TypeString,
   495  		Callback: func(args []interface{}) (interface{}, error) {
   496  			return newlines.ReplaceAllString(args[0].(string), ""), nil
   497  		},
   498  	}
   499  }
   500  
   501  // interpolationFuncFloorreturns returns the greatest integer value less than or equal to the argument
   502  func interpolationFuncFloor() ast.Function {
   503  	return ast.Function{
   504  		ArgTypes:   []ast.Type{ast.TypeFloat},
   505  		ReturnType: ast.TypeInt,
   506  		Callback: func(args []interface{}) (interface{}, error) {
   507  			return int(math.Floor(args[0].(float64))), nil
   508  		},
   509  	}
   510  }
   511  
   512  func interpolationFuncZipMap() ast.Function {
   513  	return ast.Function{
   514  		ArgTypes: []ast.Type{
   515  			ast.TypeList, // Keys
   516  			ast.TypeList, // Values
   517  		},
   518  		ReturnType: ast.TypeMap,
   519  		Callback: func(args []interface{}) (interface{}, error) {
   520  			keys := args[0].([]ast.Variable)
   521  			values := args[1].([]ast.Variable)
   522  
   523  			if len(keys) != len(values) {
   524  				return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)",
   525  					len(keys), len(values))
   526  			}
   527  
   528  			for i, val := range keys {
   529  				if val.Type != ast.TypeString {
   530  					return nil, fmt.Errorf("keys must be strings. value at position %d is %s",
   531  						i, val.Type.Printable())
   532  				}
   533  			}
   534  
   535  			result := map[string]ast.Variable{}
   536  			for i := 0; i < len(keys); i++ {
   537  				result[keys[i].Value.(string)] = values[i]
   538  			}
   539  
   540  			return result, nil
   541  		},
   542  	}
   543  }
   544  
   545  // interpolationFuncFormatList implements the "formatlist" function that does
   546  // string formatting on lists.
   547  func interpolationFuncFormatList() ast.Function {
   548  	return ast.Function{
   549  		ArgTypes:     []ast.Type{ast.TypeAny},
   550  		Variadic:     true,
   551  		VariadicType: ast.TypeAny,
   552  		ReturnType:   ast.TypeList,
   553  		Callback: func(args []interface{}) (interface{}, error) {
   554  			// Make a copy of the variadic part of args
   555  			// to avoid modifying the original.
   556  			varargs := make([]interface{}, len(args)-1)
   557  			copy(varargs, args[1:])
   558  
   559  			// Verify we have some arguments
   560  			if len(varargs) == 0 {
   561  				return nil, fmt.Errorf("no arguments to formatlist")
   562  			}
   563  
   564  			// Convert arguments that are lists into slices.
   565  			// Confirm along the way that all lists have the same length (n).
   566  			var n int
   567  			listSeen := false
   568  			for i := 1; i < len(args); i++ {
   569  				s, ok := args[i].([]ast.Variable)
   570  				if !ok {
   571  					continue
   572  				}
   573  
   574  				// Mark that we've seen at least one list
   575  				listSeen = true
   576  
   577  				// Convert the ast.Variable to a slice of strings
   578  				parts, err := listVariableValueToStringSlice(s)
   579  				if err != nil {
   580  					return nil, err
   581  				}
   582  
   583  				// otherwise the list is sent down to be indexed
   584  				varargs[i-1] = parts
   585  
   586  				// Check length
   587  				if n == 0 {
   588  					// first list we've seen
   589  					n = len(parts)
   590  					continue
   591  				}
   592  				if n != len(parts) {
   593  					return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts))
   594  				}
   595  			}
   596  
   597  			// If we didn't see a list this is an error because we
   598  			// can't determine the return value length.
   599  			if !listSeen {
   600  				return nil, fmt.Errorf(
   601  					"formatlist requires at least one list argument")
   602  			}
   603  
   604  			// Do the formatting.
   605  			format := args[0].(string)
   606  
   607  			// Generate a list of formatted strings.
   608  			list := make([]string, n)
   609  			fmtargs := make([]interface{}, len(varargs))
   610  			for i := 0; i < n; i++ {
   611  				for j, arg := range varargs {
   612  					switch arg := arg.(type) {
   613  					default:
   614  						fmtargs[j] = arg
   615  					case []string:
   616  						fmtargs[j] = arg[i]
   617  					}
   618  				}
   619  				list[i] = fmt.Sprintf(format, fmtargs...)
   620  			}
   621  			return stringSliceToVariableValue(list), nil
   622  		},
   623  	}
   624  }
   625  
   626  // interpolationFuncIndex implements the "index" function that allows one to
   627  // find the index of a specific element in a list
   628  func interpolationFuncIndex() ast.Function {
   629  	return ast.Function{
   630  		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},
   631  		ReturnType: ast.TypeInt,
   632  		Callback: func(args []interface{}) (interface{}, error) {
   633  			haystack := args[0].([]ast.Variable)
   634  			needle := args[1].(string)
   635  			for index, element := range haystack {
   636  				if needle == element.Value {
   637  					return index, nil
   638  				}
   639  			}
   640  			return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack)
   641  		},
   642  	}
   643  }
   644  
   645  // interpolationFuncBasename implements the "dirname" function.
   646  func interpolationFuncDirname() ast.Function {
   647  	return ast.Function{
   648  		ArgTypes:   []ast.Type{ast.TypeString},
   649  		ReturnType: ast.TypeString,
   650  		Callback: func(args []interface{}) (interface{}, error) {
   651  			return filepath.Dir(args[0].(string)), nil
   652  		},
   653  	}
   654  }
   655  
   656  // interpolationFuncDistinct implements the "distinct" function that
   657  // removes duplicate elements from a list.
   658  func interpolationFuncDistinct() ast.Function {
   659  	return ast.Function{
   660  		ArgTypes:     []ast.Type{ast.TypeList},
   661  		ReturnType:   ast.TypeList,
   662  		Variadic:     true,
   663  		VariadicType: ast.TypeList,
   664  		Callback: func(args []interface{}) (interface{}, error) {
   665  			var list []string
   666  
   667  			if len(args) != 1 {
   668  				return nil, fmt.Errorf("accepts only one argument.")
   669  			}
   670  
   671  			if argument, ok := args[0].([]ast.Variable); ok {
   672  				for _, element := range argument {
   673  					if element.Type != ast.TypeString {
   674  						return nil, fmt.Errorf(
   675  							"only works for flat lists, this list contains elements of %s",
   676  							element.Type.Printable())
   677  					}
   678  					list = appendIfMissing(list, element.Value.(string))
   679  				}
   680  			}
   681  
   682  			return stringSliceToVariableValue(list), nil
   683  		},
   684  	}
   685  }
   686  
   687  // helper function to add an element to a list, if it does not already exsit
   688  func appendIfMissing(slice []string, element string) []string {
   689  	for _, ele := range slice {
   690  		if ele == element {
   691  			return slice
   692  		}
   693  	}
   694  	return append(slice, element)
   695  }
   696  
   697  // for two lists `keys` and `values` of equal length, returns all elements
   698  // from `values` where the corresponding element from `keys` is in `searchset`.
   699  func interpolationFuncMatchKeys() ast.Function {
   700  	return ast.Function{
   701  		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeList, ast.TypeList},
   702  		ReturnType: ast.TypeList,
   703  		Callback: func(args []interface{}) (interface{}, error) {
   704  			output := make([]ast.Variable, 0)
   705  
   706  			values, _ := args[0].([]ast.Variable)
   707  			keys, _ := args[1].([]ast.Variable)
   708  			searchset, _ := args[2].([]ast.Variable)
   709  
   710  			if len(keys) != len(values) {
   711  				return nil, fmt.Errorf("length of keys and values should be equal")
   712  			}
   713  
   714  			for i, key := range keys {
   715  				for _, search := range searchset {
   716  					if res, err := compareSimpleVariables(key, search); err != nil {
   717  						return nil, err
   718  					} else if res == true {
   719  						output = append(output, values[i])
   720  						break
   721  					}
   722  				}
   723  			}
   724  			// if searchset is empty, then output is an empty list as well.
   725  			// if we haven't matched any key, then output is an empty list.
   726  			return output, nil
   727  		},
   728  	}
   729  }
   730  
   731  // compare two variables of the same type, i.e. non complex one, such as TypeList or TypeMap
   732  func compareSimpleVariables(a, b ast.Variable) (bool, error) {
   733  	if a.Type != b.Type {
   734  		return false, fmt.Errorf(
   735  			"won't compare items of different types %s and %s",
   736  			a.Type.Printable(), b.Type.Printable())
   737  	}
   738  	switch a.Type {
   739  	case ast.TypeString:
   740  		return a.Value.(string) == b.Value.(string), nil
   741  	default:
   742  		return false, fmt.Errorf(
   743  			"can't compare items of type %s",
   744  			a.Type.Printable())
   745  	}
   746  }
   747  
   748  // interpolationFuncJoin implements the "join" function that allows
   749  // multi-variable values to be joined by some character.
   750  func interpolationFuncJoin() ast.Function {
   751  	return ast.Function{
   752  		ArgTypes:     []ast.Type{ast.TypeString},
   753  		Variadic:     true,
   754  		VariadicType: ast.TypeList,
   755  		ReturnType:   ast.TypeString,
   756  		Callback: func(args []interface{}) (interface{}, error) {
   757  			var list []string
   758  
   759  			if len(args) < 2 {
   760  				return nil, fmt.Errorf("not enough arguments to join()")
   761  			}
   762  
   763  			for _, arg := range args[1:] {
   764  				for _, part := range arg.([]ast.Variable) {
   765  					if part.Type != ast.TypeString {
   766  						return nil, fmt.Errorf(
   767  							"only works on flat lists, this list contains elements of %s",
   768  							part.Type.Printable())
   769  					}
   770  					list = append(list, part.Value.(string))
   771  				}
   772  			}
   773  
   774  			return strings.Join(list, args[0].(string)), nil
   775  		},
   776  	}
   777  }
   778  
   779  // interpolationFuncJSONEncode implements the "jsonencode" function that encodes
   780  // a string, list, or map as its JSON representation. For now, values in the
   781  // list or map may only be strings.
   782  func interpolationFuncJSONEncode() ast.Function {
   783  	return ast.Function{
   784  		ArgTypes:   []ast.Type{ast.TypeAny},
   785  		ReturnType: ast.TypeString,
   786  		Callback: func(args []interface{}) (interface{}, error) {
   787  			var toEncode interface{}
   788  
   789  			switch typedArg := args[0].(type) {
   790  			case string:
   791  				toEncode = typedArg
   792  
   793  			case []ast.Variable:
   794  				// We preallocate the list here. Note that it's important that in
   795  				// the length 0 case, we have an empty list rather than nil, as
   796  				// they encode differently.
   797  				// XXX It would be nice to support arbitrarily nested data here. Is
   798  				// there an inverse of hil.InterfaceToVariable?
   799  				strings := make([]string, len(typedArg))
   800  
   801  				for i, v := range typedArg {
   802  					if v.Type != ast.TypeString {
   803  						return "", fmt.Errorf("list elements must be strings")
   804  					}
   805  					strings[i] = v.Value.(string)
   806  				}
   807  				toEncode = strings
   808  
   809  			case map[string]ast.Variable:
   810  				// XXX It would be nice to support arbitrarily nested data here. Is
   811  				// there an inverse of hil.InterfaceToVariable?
   812  				stringMap := make(map[string]string)
   813  				for k, v := range typedArg {
   814  					if v.Type != ast.TypeString {
   815  						return "", fmt.Errorf("map values must be strings")
   816  					}
   817  					stringMap[k] = v.Value.(string)
   818  				}
   819  				toEncode = stringMap
   820  
   821  			default:
   822  				return "", fmt.Errorf("unknown type for JSON encoding: %T", args[0])
   823  			}
   824  
   825  			jEnc, err := json.Marshal(toEncode)
   826  			if err != nil {
   827  				return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
   828  			}
   829  			return string(jEnc), nil
   830  		},
   831  	}
   832  }
   833  
   834  // interpolationFuncReplace implements the "replace" function that does
   835  // string replacement.
   836  func interpolationFuncReplace() ast.Function {
   837  	return ast.Function{
   838  		ArgTypes:   []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString},
   839  		ReturnType: ast.TypeString,
   840  		Callback: func(args []interface{}) (interface{}, error) {
   841  			s := args[0].(string)
   842  			search := args[1].(string)
   843  			replace := args[2].(string)
   844  
   845  			// We search/replace using a regexp if the string is surrounded
   846  			// in forward slashes.
   847  			if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' {
   848  				re, err := regexp.Compile(search[1 : len(search)-1])
   849  				if err != nil {
   850  					return nil, err
   851  				}
   852  
   853  				return re.ReplaceAllString(s, replace), nil
   854  			}
   855  
   856  			return strings.Replace(s, search, replace, -1), nil
   857  		},
   858  	}
   859  }
   860  
   861  func interpolationFuncLength() ast.Function {
   862  	return ast.Function{
   863  		ArgTypes:   []ast.Type{ast.TypeAny},
   864  		ReturnType: ast.TypeInt,
   865  		Variadic:   false,
   866  		Callback: func(args []interface{}) (interface{}, error) {
   867  			subject := args[0]
   868  
   869  			switch typedSubject := subject.(type) {
   870  			case string:
   871  				return len(typedSubject), nil
   872  			case []ast.Variable:
   873  				return len(typedSubject), nil
   874  			case map[string]ast.Variable:
   875  				return len(typedSubject), nil
   876  			}
   877  
   878  			return 0, fmt.Errorf("arguments to length() must be a string, list, or map")
   879  		},
   880  	}
   881  }
   882  
   883  func interpolationFuncSignum() ast.Function {
   884  	return ast.Function{
   885  		ArgTypes:   []ast.Type{ast.TypeInt},
   886  		ReturnType: ast.TypeInt,
   887  		Variadic:   false,
   888  		Callback: func(args []interface{}) (interface{}, error) {
   889  			num := args[0].(int)
   890  			switch {
   891  			case num < 0:
   892  				return -1, nil
   893  			case num > 0:
   894  				return +1, nil
   895  			default:
   896  				return 0, nil
   897  			}
   898  		},
   899  	}
   900  }
   901  
   902  // interpolationFuncSlice returns a portion of the input list between from, inclusive and to, exclusive.
   903  func interpolationFuncSlice() ast.Function {
   904  	return ast.Function{
   905  		ArgTypes: []ast.Type{
   906  			ast.TypeList, // inputList
   907  			ast.TypeInt,  // from
   908  			ast.TypeInt,  // to
   909  		},
   910  		ReturnType: ast.TypeList,
   911  		Variadic:   false,
   912  		Callback: func(args []interface{}) (interface{}, error) {
   913  			inputList := args[0].([]ast.Variable)
   914  			from := args[1].(int)
   915  			to := args[2].(int)
   916  
   917  			if from < 0 {
   918  				return nil, fmt.Errorf("from index must be >= 0")
   919  			}
   920  			if to > len(inputList) {
   921  				return nil, fmt.Errorf("to index must be <= length of the input list")
   922  			}
   923  			if from > to {
   924  				return nil, fmt.Errorf("from index must be <= to index")
   925  			}
   926  
   927  			var outputList []ast.Variable
   928  			for i, val := range inputList {
   929  				if i >= from && i < to {
   930  					outputList = append(outputList, val)
   931  				}
   932  			}
   933  			return outputList, nil
   934  		},
   935  	}
   936  }
   937  
   938  // interpolationFuncSort sorts a list of a strings lexographically
   939  func interpolationFuncSort() ast.Function {
   940  	return ast.Function{
   941  		ArgTypes:   []ast.Type{ast.TypeList},
   942  		ReturnType: ast.TypeList,
   943  		Variadic:   false,
   944  		Callback: func(args []interface{}) (interface{}, error) {
   945  			inputList := args[0].([]ast.Variable)
   946  
   947  			// Ensure that all the list members are strings and
   948  			// create a string slice from them
   949  			members := make([]string, len(inputList))
   950  			for i, val := range inputList {
   951  				if val.Type != ast.TypeString {
   952  					return nil, fmt.Errorf(
   953  						"sort() may only be used with lists of strings - %s at index %d",
   954  						val.Type.String(), i)
   955  				}
   956  
   957  				members[i] = val.Value.(string)
   958  			}
   959  
   960  			sort.Strings(members)
   961  			return stringSliceToVariableValue(members), nil
   962  		},
   963  	}
   964  }
   965  
   966  // interpolationFuncSplit implements the "split" function that allows
   967  // strings to split into multi-variable values
   968  func interpolationFuncSplit() ast.Function {
   969  	return ast.Function{
   970  		ArgTypes:   []ast.Type{ast.TypeString, ast.TypeString},
   971  		ReturnType: ast.TypeList,
   972  		Callback: func(args []interface{}) (interface{}, error) {
   973  			sep := args[0].(string)
   974  			s := args[1].(string)
   975  			elements := strings.Split(s, sep)
   976  			return stringSliceToVariableValue(elements), nil
   977  		},
   978  	}
   979  }
   980  
   981  // interpolationFuncLookup implements the "lookup" function that allows
   982  // dynamic lookups of map types within a Terraform configuration.
   983  func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function {
   984  	return ast.Function{
   985  		ArgTypes:     []ast.Type{ast.TypeMap, ast.TypeString},
   986  		ReturnType:   ast.TypeString,
   987  		Variadic:     true,
   988  		VariadicType: ast.TypeString,
   989  		Callback: func(args []interface{}) (interface{}, error) {
   990  			defaultValue := ""
   991  			defaultValueSet := false
   992  			if len(args) > 2 {
   993  				defaultValue = args[2].(string)
   994  				defaultValueSet = true
   995  			}
   996  			if len(args) > 3 {
   997  				return "", fmt.Errorf("lookup() takes no more than three arguments")
   998  			}
   999  			index := args[1].(string)
  1000  			mapVar := args[0].(map[string]ast.Variable)
  1001  
  1002  			v, ok := mapVar[index]
  1003  			if !ok {
  1004  				if defaultValueSet {
  1005  					return defaultValue, nil
  1006  				} else {
  1007  					return "", fmt.Errorf(
  1008  						"lookup failed to find '%s'",
  1009  						args[1].(string))
  1010  				}
  1011  			}
  1012  			if v.Type != ast.TypeString {
  1013  				return nil, fmt.Errorf(
  1014  					"lookup() may only be used with flat maps, this map contains elements of %s",
  1015  					v.Type.Printable())
  1016  			}
  1017  
  1018  			return v.Value.(string), nil
  1019  		},
  1020  	}
  1021  }
  1022  
  1023  // interpolationFuncElement implements the "element" function that allows
  1024  // a specific index to be looked up in a multi-variable value. Note that this will
  1025  // wrap if the index is larger than the number of elements in the multi-variable value.
  1026  func interpolationFuncElement() ast.Function {
  1027  	return ast.Function{
  1028  		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},
  1029  		ReturnType: ast.TypeString,
  1030  		Callback: func(args []interface{}) (interface{}, error) {
  1031  			list := args[0].([]ast.Variable)
  1032  			if len(list) == 0 {
  1033  				return nil, fmt.Errorf("element() may not be used with an empty list")
  1034  			}
  1035  
  1036  			index, err := strconv.Atoi(args[1].(string))
  1037  			if err != nil || index < 0 {
  1038  				return "", fmt.Errorf(
  1039  					"invalid number for index, got %s", args[1])
  1040  			}
  1041  
  1042  			resolvedIndex := index % len(list)
  1043  
  1044  			v := list[resolvedIndex]
  1045  			if v.Type != ast.TypeString {
  1046  				return nil, fmt.Errorf(
  1047  					"element() may only be used with flat lists, this list contains elements of %s",
  1048  					v.Type.Printable())
  1049  			}
  1050  			return v.Value, nil
  1051  		},
  1052  	}
  1053  }
  1054  
  1055  // interpolationFuncKeys implements the "keys" function that yields a list of
  1056  // keys of map types within a Terraform configuration.
  1057  func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
  1058  	return ast.Function{
  1059  		ArgTypes:   []ast.Type{ast.TypeMap},
  1060  		ReturnType: ast.TypeList,
  1061  		Callback: func(args []interface{}) (interface{}, error) {
  1062  			mapVar := args[0].(map[string]ast.Variable)
  1063  			keys := make([]string, 0)
  1064  
  1065  			for k, _ := range mapVar {
  1066  				keys = append(keys, k)
  1067  			}
  1068  
  1069  			sort.Strings(keys)
  1070  
  1071  			// Keys are guaranteed to be strings
  1072  			return stringSliceToVariableValue(keys), nil
  1073  		},
  1074  	}
  1075  }
  1076  
  1077  // interpolationFuncValues implements the "values" function that yields a list of
  1078  // keys of map types within a Terraform configuration.
  1079  func interpolationFuncValues(vs map[string]ast.Variable) ast.Function {
  1080  	return ast.Function{
  1081  		ArgTypes:   []ast.Type{ast.TypeMap},
  1082  		ReturnType: ast.TypeList,
  1083  		Callback: func(args []interface{}) (interface{}, error) {
  1084  			mapVar := args[0].(map[string]ast.Variable)
  1085  			keys := make([]string, 0)
  1086  
  1087  			for k, _ := range mapVar {
  1088  				keys = append(keys, k)
  1089  			}
  1090  
  1091  			sort.Strings(keys)
  1092  
  1093  			values := make([]string, len(keys))
  1094  			for index, key := range keys {
  1095  				if value, ok := mapVar[key].Value.(string); ok {
  1096  					values[index] = value
  1097  				} else {
  1098  					return "", fmt.Errorf("values(): %q has element with bad type %s",
  1099  						key, mapVar[key].Type)
  1100  				}
  1101  			}
  1102  
  1103  			variable, err := hil.InterfaceToVariable(values)
  1104  			if err != nil {
  1105  				return nil, err
  1106  			}
  1107  
  1108  			return variable.Value, nil
  1109  		},
  1110  	}
  1111  }
  1112  
  1113  // interpolationFuncBasename implements the "basename" function.
  1114  func interpolationFuncBasename() ast.Function {
  1115  	return ast.Function{
  1116  		ArgTypes:   []ast.Type{ast.TypeString},
  1117  		ReturnType: ast.TypeString,
  1118  		Callback: func(args []interface{}) (interface{}, error) {
  1119  			return filepath.Base(args[0].(string)), nil
  1120  		},
  1121  	}
  1122  }
  1123  
  1124  // interpolationFuncBase64Encode implements the "base64encode" function that
  1125  // allows Base64 encoding.
  1126  func interpolationFuncBase64Encode() ast.Function {
  1127  	return ast.Function{
  1128  		ArgTypes:   []ast.Type{ast.TypeString},
  1129  		ReturnType: ast.TypeString,
  1130  		Callback: func(args []interface{}) (interface{}, error) {
  1131  			s := args[0].(string)
  1132  			return base64.StdEncoding.EncodeToString([]byte(s)), nil
  1133  		},
  1134  	}
  1135  }
  1136  
  1137  // interpolationFuncBase64Decode implements the "base64decode" function that
  1138  // allows Base64 decoding.
  1139  func interpolationFuncBase64Decode() ast.Function {
  1140  	return ast.Function{
  1141  		ArgTypes:   []ast.Type{ast.TypeString},
  1142  		ReturnType: ast.TypeString,
  1143  		Callback: func(args []interface{}) (interface{}, error) {
  1144  			s := args[0].(string)
  1145  			sDec, err := base64.StdEncoding.DecodeString(s)
  1146  			if err != nil {
  1147  				return "", fmt.Errorf("failed to decode base64 data '%s'", s)
  1148  			}
  1149  			return string(sDec), nil
  1150  		},
  1151  	}
  1152  }
  1153  
  1154  // interpolationFuncLower implements the "lower" function that does
  1155  // string lower casing.
  1156  func interpolationFuncLower() ast.Function {
  1157  	return ast.Function{
  1158  		ArgTypes:   []ast.Type{ast.TypeString},
  1159  		ReturnType: ast.TypeString,
  1160  		Callback: func(args []interface{}) (interface{}, error) {
  1161  			toLower := args[0].(string)
  1162  			return strings.ToLower(toLower), nil
  1163  		},
  1164  	}
  1165  }
  1166  
  1167  func interpolationFuncMd5() ast.Function {
  1168  	return ast.Function{
  1169  		ArgTypes:   []ast.Type{ast.TypeString},
  1170  		ReturnType: ast.TypeString,
  1171  		Callback: func(args []interface{}) (interface{}, error) {
  1172  			s := args[0].(string)
  1173  			h := md5.New()
  1174  			h.Write([]byte(s))
  1175  			hash := hex.EncodeToString(h.Sum(nil))
  1176  			return hash, nil
  1177  		},
  1178  	}
  1179  }
  1180  
  1181  func interpolationFuncMerge() ast.Function {
  1182  	return ast.Function{
  1183  		ArgTypes:     []ast.Type{ast.TypeMap},
  1184  		ReturnType:   ast.TypeMap,
  1185  		Variadic:     true,
  1186  		VariadicType: ast.TypeMap,
  1187  		Callback: func(args []interface{}) (interface{}, error) {
  1188  			outputMap := make(map[string]ast.Variable)
  1189  
  1190  			for _, arg := range args {
  1191  				for k, v := range arg.(map[string]ast.Variable) {
  1192  					outputMap[k] = v
  1193  				}
  1194  			}
  1195  
  1196  			return outputMap, nil
  1197  		},
  1198  	}
  1199  }
  1200  
  1201  // interpolationFuncUpper implements the "upper" function that does
  1202  // string upper casing.
  1203  func interpolationFuncUpper() ast.Function {
  1204  	return ast.Function{
  1205  		ArgTypes:   []ast.Type{ast.TypeString},
  1206  		ReturnType: ast.TypeString,
  1207  		Callback: func(args []interface{}) (interface{}, error) {
  1208  			toUpper := args[0].(string)
  1209  			return strings.ToUpper(toUpper), nil
  1210  		},
  1211  	}
  1212  }
  1213  
  1214  func interpolationFuncSha1() ast.Function {
  1215  	return ast.Function{
  1216  		ArgTypes:   []ast.Type{ast.TypeString},
  1217  		ReturnType: ast.TypeString,
  1218  		Callback: func(args []interface{}) (interface{}, error) {
  1219  			s := args[0].(string)
  1220  			h := sha1.New()
  1221  			h.Write([]byte(s))
  1222  			hash := hex.EncodeToString(h.Sum(nil))
  1223  			return hash, nil
  1224  		},
  1225  	}
  1226  }
  1227  
  1228  // hexadecimal representation of sha256 sum
  1229  func interpolationFuncSha256() ast.Function {
  1230  	return ast.Function{
  1231  		ArgTypes:   []ast.Type{ast.TypeString},
  1232  		ReturnType: ast.TypeString,
  1233  		Callback: func(args []interface{}) (interface{}, error) {
  1234  			s := args[0].(string)
  1235  			h := sha256.New()
  1236  			h.Write([]byte(s))
  1237  			hash := hex.EncodeToString(h.Sum(nil))
  1238  			return hash, nil
  1239  		},
  1240  	}
  1241  }
  1242  
  1243  func interpolationFuncTrimSpace() ast.Function {
  1244  	return ast.Function{
  1245  		ArgTypes:   []ast.Type{ast.TypeString},
  1246  		ReturnType: ast.TypeString,
  1247  		Callback: func(args []interface{}) (interface{}, error) {
  1248  			trimSpace := args[0].(string)
  1249  			return strings.TrimSpace(trimSpace), nil
  1250  		},
  1251  	}
  1252  }
  1253  
  1254  func interpolationFuncBase64Sha256() ast.Function {
  1255  	return ast.Function{
  1256  		ArgTypes:   []ast.Type{ast.TypeString},
  1257  		ReturnType: ast.TypeString,
  1258  		Callback: func(args []interface{}) (interface{}, error) {
  1259  			s := args[0].(string)
  1260  			h := sha256.New()
  1261  			h.Write([]byte(s))
  1262  			shaSum := h.Sum(nil)
  1263  			encoded := base64.StdEncoding.EncodeToString(shaSum[:])
  1264  			return encoded, nil
  1265  		},
  1266  	}
  1267  }
  1268  
  1269  func interpolationFuncUUID() ast.Function {
  1270  	return ast.Function{
  1271  		ArgTypes:   []ast.Type{},
  1272  		ReturnType: ast.TypeString,
  1273  		Callback: func(args []interface{}) (interface{}, error) {
  1274  			return uuid.GenerateUUID()
  1275  		},
  1276  	}
  1277  }
  1278  
  1279  // interpolationFuncTimestamp
  1280  func interpolationFuncTimestamp() ast.Function {
  1281  	return ast.Function{
  1282  		ArgTypes:   []ast.Type{},
  1283  		ReturnType: ast.TypeString,
  1284  		Callback: func(args []interface{}) (interface{}, error) {
  1285  			return time.Now().UTC().Format(time.RFC3339), nil
  1286  		},
  1287  	}
  1288  }
  1289  
  1290  // interpolationFuncTitle implements the "title" function that returns a copy of the
  1291  // string in which first characters of all the words are capitalized.
  1292  func interpolationFuncTitle() ast.Function {
  1293  	return ast.Function{
  1294  		ArgTypes:   []ast.Type{ast.TypeString},
  1295  		ReturnType: ast.TypeString,
  1296  		Callback: func(args []interface{}) (interface{}, error) {
  1297  			toTitle := args[0].(string)
  1298  			return strings.Title(toTitle), nil
  1299  		},
  1300  	}
  1301  }
  1302  
  1303  // interpolationFuncSubstr implements the "substr" function that allows strings
  1304  // to be truncated.
  1305  func interpolationFuncSubstr() ast.Function {
  1306  	return ast.Function{
  1307  		ArgTypes: []ast.Type{
  1308  			ast.TypeString, // input string
  1309  			ast.TypeInt,    // offset
  1310  			ast.TypeInt,    // length
  1311  		},
  1312  		ReturnType: ast.TypeString,
  1313  		Callback: func(args []interface{}) (interface{}, error) {
  1314  			str := args[0].(string)
  1315  			offset := args[1].(int)
  1316  			length := args[2].(int)
  1317  
  1318  			// Interpret a negative offset as being equivalent to a positive
  1319  			// offset taken from the end of the string.
  1320  			if offset < 0 {
  1321  				offset += len(str)
  1322  			}
  1323  
  1324  			// Interpret a length of `-1` as indicating that the substring
  1325  			// should start at `offset` and continue until the end of the
  1326  			// string. Any other negative length (other than `-1`) is invalid.
  1327  			if length == -1 {
  1328  				length = len(str)
  1329  			} else if length >= 0 {
  1330  				length += offset
  1331  			} else {
  1332  				return nil, fmt.Errorf("length should be a non-negative integer")
  1333  			}
  1334  
  1335  			if offset > len(str) {
  1336  				return nil, fmt.Errorf("offset cannot be larger than the length of the string")
  1337  			}
  1338  
  1339  			if length > len(str) {
  1340  				return nil, fmt.Errorf("'offset + length' cannot be larger than the length of the string")
  1341  			}
  1342  
  1343  			return str[offset:length], nil
  1344  		},
  1345  	}
  1346  }