Merge pull request #1096 from hashicorp/f-sprintf

config: add "format' function [GH-758]
This commit is contained in:
Mitchell Hashimoto 2015-03-04 16:05:25 -08:00
commit 23609a7af5
6 changed files with 75 additions and 8 deletions

View File

@ -17,6 +17,7 @@ var Funcs map[string]ast.Function
func init() {
Funcs = map[string]ast.Function{
"file": interpolationFuncFile(),
"format": interpolationFuncFormat(),
"join": interpolationFuncJoin(),
"element": interpolationFuncElement(),
"replace": interpolationFuncReplace(),
@ -66,6 +67,21 @@ func interpolationFuncFile() ast.Function {
}
}
// interpolationFuncFormat implements the "replace" function that does
// string replacement.
func interpolationFuncFormat() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString},
Variadic: true,
VariadicType: ast.TypeAny,
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
format := args[0].(string)
return fmt.Sprintf(format, args[1:]...), nil
},
}
}
// interpolationFuncJoin implements the "join" function that allows
// multi-variable values to be joined by some character.
func interpolationFuncJoin() ast.Function {

View File

@ -70,6 +70,42 @@ func TestInterpolateFuncFile(t *testing.T) {
})
}
func TestInterpolateFuncFormat(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${format("hello")}`,
"hello",
false,
},
{
`${format("hello %s", "world")}`,
"hello world",
false,
},
{
`${format("hello %d", 42)}`,
"hello 42",
false,
},
{
`${format("hello %05d", 42)}`,
"hello 00042",
false,
},
{
`${format("hello %05d", 12345)}`,
"hello 12345",
false,
},
},
})
}
func TestInterpolateFuncJoin(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{

View File

@ -48,7 +48,8 @@ type Type uint32
const (
TypeInvalid Type = 0
TypeString Type = 1 << iota
TypeAny Type = 1 << iota
TypeString
TypeInt
TypeFloat
)

View File

@ -6,16 +6,18 @@ import "fmt"
const (
_Type_name_0 = "TypeInvalid"
_Type_name_1 = "TypeString"
_Type_name_2 = "TypeInt"
_Type_name_3 = "TypeFloat"
_Type_name_1 = "TypeAny"
_Type_name_2 = "TypeString"
_Type_name_3 = "TypeInt"
_Type_name_4 = "TypeFloat"
)
var (
_Type_index_0 = [...]uint8{0, 11}
_Type_index_1 = [...]uint8{0, 10}
_Type_index_2 = [...]uint8{0, 7}
_Type_index_3 = [...]uint8{0, 9}
_Type_index_1 = [...]uint8{0, 7}
_Type_index_2 = [...]uint8{0, 10}
_Type_index_3 = [...]uint8{0, 7}
_Type_index_4 = [...]uint8{0, 9}
)
func (i Type) String() string {
@ -28,6 +30,8 @@ func (i Type) String() string {
return _Type_name_2
case i == 8:
return _Type_name_3
case i == 16:
return _Type_name_4
default:
return fmt.Sprintf("Type(%d)", i)
}

View File

@ -174,6 +174,10 @@ func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
// Verify the args
for i, expected := range function.ArgTypes {
if expected == ast.TypeAny {
continue
}
if args[i] != expected {
cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i])
if cn != nil {
@ -188,7 +192,7 @@ func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
}
// If we're variadic, then verify the types there
if function.Variadic {
if function.Variadic && function.VariadicType != ast.TypeAny {
args = args[len(function.ArgTypes):]
for i, t := range args {
if t != function.VariadicType {

View File

@ -80,6 +80,12 @@ The supported built-in functions are:
in this file are _not_ interpolated. The contents of the file are
read as-is.
* `format(format, args...)` - Formats a string according to the given
format. The syntax for the format is standard `sprintf` syntax.
Good documentation for the syntax can be [found here](http://golang.org/pkg/fmt/).
Example to zero-prefix a count, used commonly for naming servers:
`format("web-%03d", count.index+1)`.
* `join(delim, list)` - Joins the list with the delimiter. A list is
only possible with splat variables from resources with a count
greater than one. Example: `join(",", aws_instance.foo.*.id)`