stdlib

package
v1.15.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2024 License: MIT Imports: 22 Imported by: 222

Documentation

Overview

Package stdlib is a collection of cty functions that are expected to be generally useful, and are thus factored out into this shared library in the hope that cty-using applications will have consistent behavior when using these functions.

See the parent package "function" for more information on the purpose and usage of cty functions.

This package contains both Go functions, which provide convenient access to call the functions from Go code, and the Function objects themselves. The latter follow the naming scheme of appending "Func" to the end of the function name.

Index

Constants

This section is empty.

Variables

View Source
var AbsoluteFunc = function.New(&function.Spec{
	Description: `If the given number is negative then returns its positive equivalent, or otherwise returns the given number unchanged.`,
	Params: []function.Parameter{
		{
			Name:             "num",
			Type:             cty.Number,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return args[0].Absolute(), nil
	},
})
View Source
var AddFunc = function.New(&function.Spec{
	Description: `Returns the sum of the two given numbers.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {

		defer func() {
			if r := recover(); r != nil {
				if _, ok := r.(big.ErrNaN); ok {
					ret = cty.NilVal
					err = fmt.Errorf("can't compute sum of opposing infinities")
				} else {

					panic(r)
				}
			}
		}()
		return args[0].Add(args[1]), nil
	},
})
View Source
var AndFunc = function.New(&function.Spec{
	Description: `Applies the logical AND operation to the given boolean values.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Bool,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
		{
			Name:             "b",
			Type:             cty.Bool,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return args[0].And(args[1]), nil
	},
})
View Source
var AssertNotNullFunc = function.New(&function.Spec{
	Description: "Returns the given value varbatim if it is non-null, or raises an error if it's null.",
	Params: []function.Parameter{
		{
			Name: "v",
			Type: cty.DynamicPseudoType,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		return args[0].Type(), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {

		return args[0], nil
	},
})

AssertNotNullFunc is a function which does nothing except return an error if the argument given to it is null.

This could be useful in some cases where the automatic refinment of nullability isn't precise enough, because the result is guaranteed to not be null and can therefore allow downstream comparisons to null to return a known value even if the value is otherwise unknown.

View Source
var Bytes = cty.Capsule("bytes", reflect.TypeOf([]byte(nil)))

Bytes is a capsule type that can be used with the binary functions to support applications that need to support raw buffers in addition to UTF-8 strings.

View Source
var BytesLenFunc = function.New(&function.Spec{
	Description: `Returns the total number of bytes in the given buffer.`,
	Params: []function.Parameter{
		{
			Name:             "buf",
			Type:             Bytes,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		bufPtr := args[0].EncapsulatedValue().(*[]byte)
		return cty.NumberIntVal(int64(len(*bufPtr))), nil
	},
})

BytesLen is a Function that returns the length of the buffer encapsulated in a Bytes value.

View Source
var BytesSliceFunc = function.New(&function.Spec{
	Description: `Extracts a subslice from the given buffer.`,
	Params: []function.Parameter{
		{
			Name:             "buf",
			Type:             Bytes,
			AllowDynamicType: true,
		},
		{
			Name:             "offset",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
		{
			Name:             "length",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(Bytes),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		bufPtr := args[0].EncapsulatedValue().(*[]byte)

		var offset, length int

		var err error
		err = gocty.FromCtyValue(args[1], &offset)
		if err != nil {
			return cty.NilVal, err
		}
		err = gocty.FromCtyValue(args[2], &length)
		if err != nil {
			return cty.NilVal, err
		}

		if offset < 0 || length < 0 {
			return cty.NilVal, fmt.Errorf("offset and length must be non-negative")
		}

		if offset > len(*bufPtr) {
			return cty.NilVal, fmt.Errorf(
				"offset %d is greater than total buffer length %d",
				offset, len(*bufPtr),
			)
		}

		end := offset + length

		if end > len(*bufPtr) {
			return cty.NilVal, fmt.Errorf(
				"offset %d + length %d is greater than total buffer length %d",
				offset, length, len(*bufPtr),
			)
		}

		return BytesVal((*bufPtr)[offset:end]), nil
	},
})

BytesSlice is a Function that returns a slice of the given Bytes value.

View Source
var CSVDecodeFunc = function.New(&function.Spec{
	Description: `Parses the given string as Comma Separated Values (as defined by RFC 4180) and returns a map of objects representing the table of data, using the first row as a header row to define the object attributes.`,
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		str := args[0]
		if !str.IsKnown() {
			return cty.DynamicPseudoType, nil
		}

		r := strings.NewReader(str.AsString())
		cr := csv.NewReader(r)
		headers, err := cr.Read()
		if err == io.EOF {
			return cty.DynamicPseudoType, fmt.Errorf("missing header line")
		}
		if err != nil {
			return cty.DynamicPseudoType, csvError(err)
		}

		atys := make(map[string]cty.Type, len(headers))
		for _, name := range headers {
			if _, exists := atys[name]; exists {
				return cty.DynamicPseudoType, fmt.Errorf("duplicate column name %q", name)
			}
			atys[name] = cty.String
		}
		return cty.List(cty.Object(atys)), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		ety := retType.ElementType()
		atys := ety.AttributeTypes()
		str := args[0]
		r := strings.NewReader(str.AsString())
		cr := csv.NewReader(r)
		cr.FieldsPerRecord = len(atys)

		headers, err := cr.Read()
		if err != nil {
			return cty.DynamicVal, err
		}

		var rows []cty.Value
		for {
			cols, err := cr.Read()
			if err == io.EOF {
				break
			}
			if err != nil {
				return cty.DynamicVal, csvError(err)
			}

			vals := make(map[string]cty.Value, len(cols))
			for i, str := range cols {
				name := headers[i]
				vals[name] = cty.StringVal(str)
			}
			rows = append(rows, cty.ObjectVal(vals))
		}

		if len(rows) == 0 {
			return cty.ListValEmpty(ety), nil
		}
		return cty.ListVal(rows), nil
	},
})
View Source
var CeilFunc = function.New(&function.Spec{
	Description: `Returns the smallest whole number that is greater than or equal to the given value.`,
	Params: []function.Parameter{
		{
			Name: "num",
			Type: cty.Number,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		f := args[0].AsBigFloat()

		if f.IsInf() {
			return cty.NumberVal(f), nil
		}

		i, acc := f.Int(nil)
		switch acc {
		case big.Exact, big.Above:

		case big.Below:
			i.Add(i, big.NewInt(1))
		}

		return cty.NumberVal(f.SetInt(i)), nil
	},
})

CeilFunc is a function that returns the closest whole number greater than or equal to the given value.

View Source
var ChompFunc = function.New(&function.Spec{
	Description: "Removes one or more newline characters from the end of the given string.",
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`)
		return cty.StringVal(newlines.ReplaceAllString(args[0].AsString(), "")), nil
	},
})

ChompFunc is a function that removes newline characters at the end of a string.

View Source
var ChunklistFunc = function.New(&function.Spec{
	Description: `Splits a single list into multiple lists where each has at most the given number of elements.`,
	Params: []function.Parameter{
		{
			Name:        "list",
			Description: `The list to split into chunks.`,
			Type:        cty.List(cty.DynamicPseudoType),
			AllowMarked: true,
		},
		{
			Name:        "size",
			Description: `The maximum length of each chunk. All but the last element of the result is guaranteed to be of exactly this size.`,
			Type:        cty.Number,
			AllowMarked: true,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		return cty.List(args[0].Type()), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		listVal := args[0]
		sizeVal := args[1]
		listVal, listMarks := listVal.Unmark()
		sizeVal, sizeMarks := sizeVal.Unmark()

		retMarks := cty.NewValueMarks(listMarks, sizeMarks)

		var size int
		err = gocty.FromCtyValue(sizeVal, &size)
		if err != nil {
			return cty.NilVal, fmt.Errorf("invalid size: %s", err)
		}

		if size < 0 {
			return cty.NilVal, errors.New("the size argument must be positive")
		}

		if listVal.LengthInt() == 0 {
			return cty.ListValEmpty(listVal.Type()).WithMarks(retMarks), nil
		}

		output := make([]cty.Value, 0)

		if size == 0 {
			output = append(output, listVal)
			return cty.ListVal(output).WithMarks(retMarks), nil
		}

		chunk := make([]cty.Value, 0)

		l := listVal.LengthInt()
		i := 0

		for it := listVal.ElementIterator(); it.Next(); {
			_, v := it.Element()
			chunk = append(chunk, v)

			if (i+1)%size == 0 || (i+1) == l {
				output = append(output, cty.ListVal(chunk))
				chunk = make([]cty.Value, 0)
			}
			i++
		}

		return cty.ListVal(output).WithMarks(retMarks), nil
	},
})

ChunklistFunc is a function that splits a single list into fixed-size chunks, returning a list of lists.

View Source
var CoalesceFunc = function.New(&function.Spec{
	Description: `Returns the first of the given arguments that isn't null, or raises an error if there are no non-null arguments.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:             "vals",
		Type:             cty.DynamicPseudoType,
		AllowUnknown:     true,
		AllowDynamicType: true,
		AllowNull:        true,
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		argTypes := make([]cty.Type, len(args))
		for i, val := range args {
			argTypes[i] = val.Type()
		}
		retType, _ := convert.UnifyUnsafe(argTypes)
		if retType == cty.NilType {
			return cty.NilType, fmt.Errorf("all arguments must have the same type")
		}
		return retType, nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		for _, argVal := range args {
			if !argVal.IsKnown() {
				return cty.UnknownVal(retType), nil
			}
			if argVal.IsNull() {
				continue
			}

			return convert.Convert(argVal, retType)
		}
		return cty.NilVal, fmt.Errorf("no non-null arguments")
	},
})
View Source
var CoalesceListFunc = function.New(&function.Spec{
	Description: `Returns the first of the given sequences that has a length greater than zero.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:             "vals",
		Description:      `List or tuple values to test in the given order.`,
		Type:             cty.DynamicPseudoType,
		AllowUnknown:     true,
		AllowDynamicType: true,
		AllowNull:        true,
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		if len(args) == 0 {
			return cty.NilType, errors.New("at least one argument is required")
		}

		argTypes := make([]cty.Type, len(args))

		for i, arg := range args {

			if !arg.IsKnown() {
				return cty.DynamicPseudoType, nil
			}
			ty := arg.Type()

			if !ty.IsListType() && !ty.IsTupleType() {
				return cty.NilType, errors.New("coalescelist arguments must be lists or tuples")
			}

			argTypes[i] = arg.Type()
		}

		last := argTypes[0]

		for _, next := range argTypes[1:] {
			if !next.Equals(last) {
				return cty.DynamicPseudoType, nil
			}
		}

		return last, nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		for _, arg := range args {
			if !arg.IsKnown() {

				return cty.UnknownVal(retType), nil
			}

			if arg.IsNull() {
				continue
			}

			if arg.LengthInt() > 0 {
				return arg, nil
			}
		}

		return cty.NilVal, errors.New("no non-null arguments")
	},
})

CoalesceListFunc is a function that takes any number of list arguments and returns the first one that isn't empty.

View Source
var CompactFunc = function.New(&function.Spec{
	Description: `Removes all empty string elements from the given list of strings.`,
	Params: []function.Parameter{
		{
			Name: "list",
			Type: cty.List(cty.String),
		},
	},
	Type:         function.StaticReturnType(cty.List(cty.String)),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		listVal := args[0]
		if !listVal.IsWhollyKnown() {

			return cty.UnknownVal(retType), nil
		}

		var outputList []cty.Value

		for it := listVal.ElementIterator(); it.Next(); {
			_, v := it.Element()
			if v.IsNull() || v.AsString() == "" {
				continue
			}
			outputList = append(outputList, v)
		}

		if len(outputList) == 0 {
			return cty.ListValEmpty(cty.String), nil
		}

		return cty.ListVal(outputList), nil
	},
})

CompactFunc is a function that takes a list of strings and returns a new list with any empty string elements removed.

View Source
var ConcatFunc = function.New(&function.Spec{
	Description: `Concatenates together all of the given lists or tuples into a single sequence, preserving the input order.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:        "seqs",
		Type:        cty.DynamicPseudoType,
		AllowMarked: true,
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		if len(args) == 0 {
			return cty.NilType, fmt.Errorf("at least one argument is required")
		}

		if args[0].Type().IsListType() {

			tys := make([]cty.Type, len(args))
			for i, val := range args {
				ty := val.Type()
				if !ty.IsListType() {
					tys = nil
					break
				}
				tys[i] = ty
			}

			if tys != nil {
				commonType, _ := convert.UnifyUnsafe(tys)
				if commonType != cty.NilType {
					return commonType, nil
				}
			}
		}

		etys := make([]cty.Type, 0, len(args))
		for i, val := range args {

			val, _ := val.UnmarkDeep()

			ety := val.Type()
			switch {
			case ety.IsTupleType():
				etys = append(etys, ety.TupleElementTypes()...)
			case ety.IsListType():
				if !val.IsKnown() {

					return cty.DynamicPseudoType, nil
				}

				l := val.LengthInt()
				subEty := ety.ElementType()
				for j := 0; j < l; j++ {
					etys = append(etys, subEty)
				}
			default:
				return cty.NilType, function.NewArgErrorf(
					i, "all arguments must be lists or tuples; got %s",
					ety.FriendlyName(),
				)
			}
		}
		return cty.Tuple(etys), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		switch {
		case retType.IsListType():

			vals := make([]cty.Value, 0, len(args))
			var markses []cty.ValueMarks // remember any marked lists we find
			for i, list := range args {
				list, err = convert.Convert(list, retType)
				if err != nil {

					return cty.NilVal, function.NewArgError(i, err)
				}

				list, listMarks := list.Unmark()
				if len(listMarks) > 0 {
					markses = append(markses, listMarks)
				}

				it := list.ElementIterator()
				for it.Next() {
					_, v := it.Element()
					vals = append(vals, v)
				}
			}
			if len(vals) == 0 {
				return cty.ListValEmpty(retType.ElementType()).WithMarks(markses...), nil
			}

			return cty.ListVal(vals).WithMarks(markses...), nil
		case retType.IsTupleType():

			vals := make([]cty.Value, 0, len(args))
			var markses []cty.ValueMarks // remember any marked seqs we find

			for _, seq := range args {
				seq, seqMarks := seq.Unmark()
				if len(seqMarks) > 0 {
					markses = append(markses, seqMarks)
				}

				it := seq.ElementIterator()
				for it.Next() {
					_, v := it.Element()
					vals = append(vals, v)
				}
			}

			return cty.TupleVal(vals).WithMarks(markses...), nil
		default:

			panic("unsupported return type")
		}
	},
})
View Source
var ContainsFunc = function.New(&function.Spec{
	Description: `Returns true if the given value is a value in the given list, tuple, or set, or false otherwise.`,
	Params: []function.Parameter{
		{
			Name: "list",
			Type: cty.DynamicPseudoType,
		},
		{
			Name: "value",
			Type: cty.DynamicPseudoType,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		arg := args[0]
		ty := arg.Type()

		if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() {
			return cty.NilVal, errors.New("argument must be list, tuple, or set")
		}

		if args[0].IsNull() {
			return cty.NilVal, errors.New("cannot search a nil list or set")
		}

		if args[0].LengthInt() == 0 {
			return cty.False, nil
		}

		if !args[0].IsKnown() || !args[1].IsKnown() {
			return cty.UnknownVal(cty.Bool), nil
		}

		containsUnknown := false
		for it := args[0].ElementIterator(); it.Next(); {
			_, v := it.Element()
			eq := args[1].Equals(v)
			if !eq.IsKnown() {

				containsUnknown = true
				continue
			}
			if eq.True() {
				return cty.True, nil
			}
		}

		if containsUnknown {
			return cty.UnknownVal(cty.Bool), nil
		}

		return cty.False, nil
	},
})

ContainsFunc is a function that determines whether a given list or set contains a given single value as one of its elements.

View Source
var DistinctFunc = function.New(&function.Spec{
	Description: `Removes any duplicate values from the given list, preserving the order of remaining elements.`,
	Params: []function.Parameter{
		{
			Name: "list",
			Type: cty.List(cty.DynamicPseudoType),
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		return args[0].Type(), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		listVal := args[0]

		if !listVal.IsWhollyKnown() {
			return cty.UnknownVal(retType), nil
		}
		var list []cty.Value

		for it := listVal.ElementIterator(); it.Next(); {
			_, v := it.Element()
			list, err = appendIfMissing(list, v)
			if err != nil {
				return cty.NilVal, err
			}
		}

		if len(list) == 0 {
			return cty.ListValEmpty(retType.ElementType()), nil
		}
		return cty.ListVal(list), nil
	},
})

DistinctFunc is a function that takes a list and returns a new list with any duplicate elements removed.

View Source
var DivideFunc = function.New(&function.Spec{
	Description: `Divides the first given number by the second.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {

		defer func() {
			if r := recover(); r != nil {
				if _, ok := r.(big.ErrNaN); ok {
					ret = cty.NilVal
					err = fmt.Errorf("can't divide zero by zero or infinity by infinity")
				} else {

					panic(r)
				}
			}
		}()

		return args[0].Divide(args[1]), nil
	},
})
View Source
var ElementFunc = function.New(&function.Spec{
	Description: `Returns the element with the given index from the given list or tuple, applying the modulo operation to the given index if it's greater than the number of elements.`,
	Params: []function.Parameter{
		{
			Name:        "list",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
		{
			Name: "index",
			Type: cty.Number,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		list := args[0]

		listTy := list.Type()
		switch {
		case listTy.IsListType():
			return listTy.ElementType(), nil
		case listTy.IsTupleType():
			if !args[1].IsKnown() {

				return cty.DynamicPseudoType, nil
			}

			etys := listTy.TupleElementTypes()
			var index int
			err := gocty.FromCtyValue(args[1], &index)
			if err != nil {

				return cty.DynamicPseudoType, fmt.Errorf("invalid index: %s", err)
			}
			if len(etys) == 0 {
				return cty.DynamicPseudoType, errors.New("cannot use element function with an empty list")
			}
			index = index % len(etys)
			return etys[index], nil
		default:
			return cty.DynamicPseudoType, fmt.Errorf("cannot read elements from %s", listTy.FriendlyName())
		}
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		var index int
		err := gocty.FromCtyValue(args[1], &index)
		if err != nil {

			return cty.DynamicVal, fmt.Errorf("invalid index: %s", err)
		}

		input, marks := args[0].Unmark()
		if !input.IsKnown() {
			return cty.UnknownVal(retType), nil
		}

		l := input.LengthInt()
		if l == 0 {
			return cty.DynamicVal, errors.New("cannot use element function with an empty list")
		}
		index = index % l
		if index < 0 {
			index += l
		}

		return input.Index(cty.NumberIntVal(int64(index))).WithMarks(marks), nil
	},
})
View Source
var EqualFunc = function.New(&function.Spec{
	Description: `Returns true if the two given values are equal, or false otherwise.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.DynamicPseudoType,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowNull:        true,
		},
		{
			Name:             "b",
			Type:             cty.DynamicPseudoType,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowNull:        true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].Equals(args[1]), nil
	},
})
View Source
var FlattenFunc = function.New(&function.Spec{
	Description: `Transforms a list, set, or tuple value into a tuple by replacing any given elements that are themselves sequences with a flattened tuple of all of the nested elements concatenated together.`,
	Params: []function.Parameter{
		{
			Name:        "list",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		if !args[0].IsWhollyKnown() {
			return cty.DynamicPseudoType, nil
		}

		argTy := args[0].Type()
		if !argTy.IsListType() && !argTy.IsSetType() && !argTy.IsTupleType() {
			return cty.NilType, errors.New("can only flatten lists, sets and tuples")
		}

		retVal, _, known := flattener(args[0])
		if !known {
			return cty.DynamicPseudoType, nil
		}

		tys := make([]cty.Type, len(retVal))
		for i, ty := range retVal {
			tys[i] = ty.Type()
		}
		return cty.Tuple(tys), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		inputList := args[0]

		if unmarked, marks := inputList.Unmark(); unmarked.LengthInt() == 0 {
			return cty.EmptyTupleVal.WithMarks(marks), nil
		}

		out, markses, known := flattener(inputList)
		if !known {
			return cty.UnknownVal(retType).WithMarks(markses...), nil
		}

		return cty.TupleVal(out).WithMarks(markses...), nil
	},
})

FlattenFunc is a function that takes a list and replaces any elements that are lists with a flattened sequence of the list contents.

View Source
var FloorFunc = function.New(&function.Spec{
	Description: `Returns the greatest whole number that is less than or equal to the given value.`,
	Params: []function.Parameter{
		{
			Name: "num",
			Type: cty.Number,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		f := args[0].AsBigFloat()

		if f.IsInf() {
			return cty.NumberVal(f), nil
		}

		i, acc := f.Int(nil)
		switch acc {
		case big.Exact, big.Below:

		case big.Above:
			i.Sub(i, big.NewInt(1))
		}

		return cty.NumberVal(f.SetInt(i)), nil
	},
})

FloorFunc is a function that returns the closest whole number lesser than or equal to the given value.

View Source
var FormatDateFunc = function.New(&function.Spec{
	Description: `Formats a timestamp given in RFC 3339 syntax into another timestamp in some other machine-oriented time syntax, as described in the format string.`,
	Params: []function.Parameter{
		{
			Name: "format",
			Type: cty.String,
		},
		{
			Name: "time",
			Type: cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		formatStr := args[0].AsString()
		timeStr := args[1].AsString()
		t, err := parseTimestamp(timeStr)
		if err != nil {
			return cty.DynamicVal, function.NewArgError(1, err)
		}

		var buf bytes.Buffer
		sc := bufio.NewScanner(strings.NewReader(formatStr))
		sc.Split(splitDateFormat)
		const esc = '\''
		for sc.Scan() {
			tok := sc.Bytes()

			switch {
			case tok[0] == esc:
				if tok[len(tok)-1] != esc || len(tok) == 1 {
					return cty.DynamicVal, function.NewArgErrorf(0, "unterminated literal '")
				}
				if len(tok) == 2 {

					buf.WriteByte(esc)
				} else {

					raw := tok[1 : len(tok)-1]
					for i := 0; i < len(raw); i++ {
						buf.WriteByte(raw[i])
						if raw[i] == esc {
							i++
						}
					}
				}

			case startsDateFormatVerb(tok[0]):
				switch tok[0] {
				case 'Y':
					y := t.Year()
					switch len(tok) {
					case 2:
						fmt.Fprintf(&buf, "%02d", y%100)
					case 4:
						fmt.Fprintf(&buf, "%04d", y)
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: year must either be \"YY\" or \"YYYY\"", tok)
					}
				case 'M':
					m := t.Month()
					switch len(tok) {
					case 1:
						fmt.Fprintf(&buf, "%d", m)
					case 2:
						fmt.Fprintf(&buf, "%02d", m)
					case 3:
						buf.WriteString(m.String()[:3])
					case 4:
						buf.WriteString(m.String())
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: month must be \"M\", \"MM\", \"MMM\", or \"MMMM\"", tok)
					}
				case 'D':
					d := t.Day()
					switch len(tok) {
					case 1:
						fmt.Fprintf(&buf, "%d", d)
					case 2:
						fmt.Fprintf(&buf, "%02d", d)
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of month must either be \"D\" or \"DD\"", tok)
					}
				case 'E':
					d := t.Weekday()
					switch len(tok) {
					case 3:
						buf.WriteString(d.String()[:3])
					case 4:
						buf.WriteString(d.String())
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of week must either be \"EEE\" or \"EEEE\"", tok)
					}
				case 'h':
					h := t.Hour()
					switch len(tok) {
					case 1:
						fmt.Fprintf(&buf, "%d", h)
					case 2:
						fmt.Fprintf(&buf, "%02d", h)
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 24-hour must either be \"h\" or \"hh\"", tok)
					}
				case 'H':
					h := t.Hour() % 12
					if h == 0 {
						h = 12
					}
					switch len(tok) {
					case 1:
						fmt.Fprintf(&buf, "%d", h)
					case 2:
						fmt.Fprintf(&buf, "%02d", h)
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 12-hour must either be \"H\" or \"HH\"", tok)
					}
				case 'A', 'a':
					if len(tok) != 2 {
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: must be \"%s%s\"", tok, tok[0:1], tok[0:1])
					}
					upper := tok[0] == 'A'
					switch t.Hour() / 12 {
					case 0:
						if upper {
							buf.WriteString("AM")
						} else {
							buf.WriteString("am")
						}
					case 1:
						if upper {
							buf.WriteString("PM")
						} else {
							buf.WriteString("pm")
						}
					}
				case 'm':
					m := t.Minute()
					switch len(tok) {
					case 1:
						fmt.Fprintf(&buf, "%d", m)
					case 2:
						fmt.Fprintf(&buf, "%02d", m)
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: minute must either be \"m\" or \"mm\"", tok)
					}
				case 's':
					s := t.Second()
					switch len(tok) {
					case 1:
						fmt.Fprintf(&buf, "%d", s)
					case 2:
						fmt.Fprintf(&buf, "%02d", s)
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: second must either be \"s\" or \"ss\"", tok)
					}
				case 'Z':

					switch len(tok) {
					case 1:
						buf.WriteString(t.Format("Z07:00"))
					case 3:
						str := t.Format("-0700")
						switch str {
						case "+0000":
							buf.WriteString("UTC")
						default:
							buf.WriteString(str)
						}
					case 4:
						buf.WriteString(t.Format("-0700"))
					case 5:
						buf.WriteString(t.Format("-07:00"))
					default:
						return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: timezone must be Z, ZZZZ, or ZZZZZ", tok)
					}
				default:
					return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q", tok)
				}

			default:

				buf.Write(tok)
			}
		}

		return cty.StringVal(buf.String()), nil
	},
})
View Source
var FormatFunc = function.New(&function.Spec{
	Description: `Constructs a string by applying formatting verbs to a series of arguments, using a similar syntax to the C function \"printf\".`,
	Params: []function.Parameter{
		{
			Name: "format",
			Type: cty.String,
		},
	},
	VarParam: &function.Parameter{
		Name:         "args",
		Type:         cty.DynamicPseudoType,
		AllowNull:    true,
		AllowUnknown: true,
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		for _, arg := range args[1:] {
			if !arg.IsWhollyKnown() {

				f := args[0].AsString()
				if idx := strings.IndexByte(f, '%'); idx > 0 {
					prefix := f[:idx]
					return cty.UnknownVal(cty.String).Refine().StringPrefix(prefix).NewValue(), nil
				}
				return cty.UnknownVal(cty.String), nil
			}
		}
		str, err := formatFSM(args[0].AsString(), args[1:])
		return cty.StringVal(str), err
	},
})
View Source
var FormatListFunc = function.New(&function.Spec{
	Description: `Constructs a list of strings by applying formatting verbs to a series of arguments, using a similar syntax to the C function \"printf\".`,
	Params: []function.Parameter{
		{
			Name: "format",
			Type: cty.String,
		},
	},
	VarParam: &function.Parameter{
		Name:         "args",
		Type:         cty.DynamicPseudoType,
		AllowNull:    true,
		AllowUnknown: true,
	},
	Type:         function.StaticReturnType(cty.List(cty.String)),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		fmtVal := args[0]
		args = args[1:]

		if len(args) == 0 {

			result, err := Format(fmtVal, args...)
			return cty.ListVal([]cty.Value{result}), err
		}

		fmtStr := fmtVal.AsString()

		iterLen := -1
		lenChooser := -1
		iterators := make([]cty.ElementIterator, len(args))
		singleVals := make([]cty.Value, len(args))
		unknowns := make([]bool, len(args))
		for i, arg := range args {
			argTy := arg.Type()
			switch {
			case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull():
				if !argTy.IsTupleType() && !(arg.IsKnown() && arg.Length().IsKnown()) {

					unknowns[i] = true
					continue
				}
				thisLen := arg.LengthInt()
				if iterLen == -1 {
					iterLen = thisLen
					lenChooser = i
				} else {
					if thisLen != iterLen {
						return cty.NullVal(cty.List(cty.String)), function.NewArgErrorf(
							i+1,
							"argument %d has length %d, which is inconsistent with argument %d of length %d",
							i+1, thisLen,
							lenChooser+1, iterLen,
						)
					}
				}
				if !arg.IsKnown() {

					unknowns[i] = true
					continue
				}
				iterators[i] = arg.ElementIterator()
			case arg == cty.DynamicVal:
				unknowns[i] = true
			default:
				singleVals[i] = arg
			}
		}

		for _, isUnk := range unknowns {
			if isUnk {
				return cty.UnknownVal(retType), nil
			}
		}

		if iterLen == 0 {

			return cty.ListValEmpty(cty.String), nil
		}

		if iterLen == -1 {

			iterLen = 1
		}

		ret := make([]cty.Value, 0, iterLen)
		fmtArgs := make([]cty.Value, len(iterators))
	Results:
		for iterIdx := 0; iterIdx < iterLen; iterIdx++ {

			for i := range fmtArgs {
				switch {
				case iterators[i] != nil:
					iterator := iterators[i]
					iterator.Next()
					_, val := iterator.Element()
					fmtArgs[i] = val
				default:
					fmtArgs[i] = singleVals[i]
				}

				if !fmtArgs[i].IsWhollyKnown() {

					ret = append(ret, cty.UnknownVal(cty.String).RefineNotNull())
					continue Results
				}
			}

			str, err := formatFSM(fmtStr, fmtArgs)
			if err != nil {
				return cty.NullVal(cty.List(cty.String)), fmt.Errorf(
					"error on format iteration %d: %s", iterIdx, err,
				)
			}

			ret = append(ret, cty.StringVal(str))
		}

		return cty.ListVal(ret), nil
	},
})
View Source
var GreaterThanFunc = function.New(&function.Spec{
	Description: `Returns true if and only if the second number is greater than the first.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].GreaterThan(args[1]), nil
	},
})
View Source
var GreaterThanOrEqualToFunc = function.New(&function.Spec{
	Description: `Returns true if and only if the second number is greater than or equal to the first.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].GreaterThanOrEqualTo(args[1]), nil
	},
})
View Source
var HasIndexFunc = function.New(&function.Spec{
	Description: `Returns true if if the given collection can be indexed with the given key without producing an error, or false otherwise.`,
	Params: []function.Parameter{
		{
			Name:             "collection",
			Type:             cty.DynamicPseudoType,
			AllowDynamicType: true,
		},
		{
			Name:             "key",
			Type:             cty.DynamicPseudoType,
			AllowDynamicType: true,
		},
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		collTy := args[0].Type()
		if !(collTy.IsTupleType() || collTy.IsListType() || collTy.IsMapType() || collTy == cty.DynamicPseudoType) {
			return cty.NilType, fmt.Errorf("collection must be a list, a map or a tuple")
		}
		return cty.Bool, nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].HasIndex(args[1]), nil
	},
})
View Source
var IndentFunc = function.New(&function.Spec{
	Description: "Adds a given number of spaces after each newline character in the given string.",
	Params: []function.Parameter{
		{
			Name:        "spaces",
			Description: "Number of spaces to add after each newline character.",
			Type:        cty.Number,
		},
		{
			Name:        "str",
			Description: "The string to transform.",
			Type:        cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		var spaces int
		if err := gocty.FromCtyValue(args[0], &spaces); err != nil {
			return cty.UnknownVal(cty.String), err
		}
		data := args[1].AsString()
		pad := strings.Repeat(" ", spaces)
		return cty.StringVal(strings.Replace(data, "\n", "\n"+pad, -1)), nil
	},
})

IndentFunc is a function that adds a given number of spaces to the beginnings of all but the first line in a given multi-line string.

View Source
var IndexFunc = function.New(&function.Spec{
	Description: `Returns the element with the given key from the given collection, or raises an error if there is no such element.`,
	Params: []function.Parameter{
		{
			Name: "collection",
			Type: cty.DynamicPseudoType,
		},
		{
			Name:             "key",
			Type:             cty.DynamicPseudoType,
			AllowDynamicType: true,
		},
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		collTy := args[0].Type()
		key := args[1]
		keyTy := key.Type()
		switch {
		case collTy.IsTupleType():
			if keyTy != cty.Number && keyTy != cty.DynamicPseudoType {
				return cty.NilType, fmt.Errorf("key for tuple must be number")
			}
			if !key.IsKnown() {
				return cty.DynamicPseudoType, nil
			}
			var idx int
			err := gocty.FromCtyValue(key, &idx)
			if err != nil {
				return cty.NilType, fmt.Errorf("invalid key for tuple: %s", err)
			}

			etys := collTy.TupleElementTypes()

			if idx >= len(etys) || idx < 0 {
				return cty.NilType, fmt.Errorf("key must be between 0 and %d inclusive", len(etys))
			}

			return etys[idx], nil

		case collTy.IsListType():
			if keyTy != cty.Number && keyTy != cty.DynamicPseudoType {
				return cty.NilType, fmt.Errorf("key for list must be number")
			}

			return collTy.ElementType(), nil

		case collTy.IsMapType():
			if keyTy != cty.String && keyTy != cty.DynamicPseudoType {
				return cty.NilType, fmt.Errorf("key for map must be string")
			}

			return collTy.ElementType(), nil

		default:
			return cty.NilType, fmt.Errorf("collection must be a list, a map or a tuple")
		}
	},
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		has, err := HasIndex(args[0], args[1])
		if err != nil {
			return cty.NilVal, err
		}
		if has.False() {
			return cty.NilVal, fmt.Errorf("invalid index")
		}

		return args[0].Index(args[1]), nil
	},
})
View Source
var IntFunc = function.New(&function.Spec{
	Description: `Discards any fractional portion of the given number.`,
	Params: []function.Parameter{
		{
			Name:             "num",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		bf := args[0].AsBigFloat()
		if bf.IsInt() {
			return args[0], nil
		}
		bi, _ := bf.Int(nil)
		bf = (&big.Float{}).SetInt(bi)
		return cty.NumberVal(bf), nil
	},
})
View Source
var JSONDecodeFunc = function.New(&function.Spec{
	Description: `Parses the given string as JSON and returns a value corresponding to what the JSON document describes.`,
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		str := args[0]
		if !str.IsKnown() {

			rng := str.Range()
			if prefix := strings.TrimSpace(rng.StringPrefix()); prefix != "" {

				switch r, _ := utf8.DecodeRuneInString(prefix); r {
				case '{', '[':

				case '"':

					return cty.String, nil
				case 't', 'f':

					return cty.Bool, nil
				case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':

					return cty.Number, nil
				case 'n':

				default:

					return cty.NilType, function.NewArgErrorf(0, "a JSON document cannot begin with the character %q", r)
				}
			}
			return cty.DynamicPseudoType, nil
		}

		buf := []byte(str.AsString())
		return json.ImpliedType(buf)
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		buf := []byte(args[0].AsString())
		return json.Unmarshal(buf, retType)
	},
})
View Source
var JSONEncodeFunc = function.New(&function.Spec{
	Description: `Returns a string containing a JSON representation of the given value.`,
	Params: []function.Parameter{
		{
			Name:             "val",
			Type:             cty.DynamicPseudoType,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowNull:        true,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		val := args[0]
		if !val.IsWhollyKnown() {

			valRng := val.Range()
			if valRng.CouldBeNull() {

				return cty.UnknownVal(retType), nil
			}
			b := cty.UnknownVal(retType).Refine()
			ty := valRng.TypeConstraint()
			switch {
			case ty == cty.String:
				b = b.StringPrefixFull(`"`)
			case ty.IsObjectType() || ty.IsMapType():
				b = b.StringPrefixFull("{")
			case ty.IsTupleType() || ty.IsListType() || ty.IsSetType():
				b = b.StringPrefixFull("[")
			}
			return b.NewValue(), nil
		}

		if val.IsNull() {
			return cty.StringVal("null"), nil
		}

		buf, err := json.Marshal(val, val.Type())
		if err != nil {
			return cty.NilVal, err
		}

		buf = bytes.TrimSpace(buf)

		return cty.StringVal(string(buf)), nil
	},
})
View Source
var JoinFunc = function.New(&function.Spec{
	Description: "Concatenates together the elements of all given lists with a delimiter, producing a single string.",
	Params: []function.Parameter{
		{
			Name:        "separator",
			Description: "Delimiter to insert between the given strings.",
			Type:        cty.String,
		},
	},
	VarParam: &function.Parameter{
		Name:        "lists",
		Description: "One or more lists of strings to join.",
		Type:        cty.List(cty.String),
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		sep := args[0].AsString()
		listVals := args[1:]
		if len(listVals) < 1 {
			return cty.UnknownVal(cty.String), fmt.Errorf("at least one list is required")
		}

		l := 0
		for _, list := range listVals {
			if !list.IsWhollyKnown() {
				return cty.UnknownVal(cty.String), nil
			}
			l += list.LengthInt()
		}

		items := make([]string, 0, l)
		for ai, list := range listVals {
			ei := 0
			for it := list.ElementIterator(); it.Next(); {
				_, val := it.Element()
				if val.IsNull() {
					if len(listVals) > 1 {
						return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d of list %d is null; cannot concatenate null values", ei, ai+1)
					}
					return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d is null; cannot concatenate null values", ei)
				}
				items = append(items, val.AsString())
				ei++
			}
		}

		return cty.StringVal(strings.Join(items, sep)), nil
	},
})
View Source
var KeysFunc = function.New(&function.Spec{
	Description: `Returns a list of the keys of the given map in lexicographical order.`,
	Params: []function.Parameter{
		{
			Name:         "inputMap",
			Description:  `The map to extract keys from. May instead be an object-typed value, in which case the result is a tuple of the object attributes.`,
			Type:         cty.DynamicPseudoType,
			AllowUnknown: true,
			AllowMarked:  true,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		ty := args[0].Type()
		switch {
		case ty.IsMapType():
			return cty.List(cty.String), nil
		case ty.IsObjectType():
			atys := ty.AttributeTypes()
			if len(atys) == 0 {
				return cty.EmptyTuple, nil
			}

			etys := make([]cty.Type, len(atys))
			for i := range etys {
				etys[i] = cty.String
			}
			return cty.Tuple(etys), nil
		default:
			return cty.DynamicPseudoType, function.NewArgErrorf(0, "must have map or object type")
		}
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {

		m, marks := args[0].Unmark()
		var keys []cty.Value

		switch {
		case m.Type().IsObjectType():
			// In this case we allow unknown values so we must work only with
			// the attribute _types_, not with the value itself.
			var names []string
			for name := range m.Type().AttributeTypes() {
				names = append(names, name)
			}
			sort.Strings(names)
			if len(names) == 0 {
				return cty.EmptyTupleVal.WithMarks(marks), nil
			}
			keys = make([]cty.Value, len(names))
			for i, name := range names {
				keys[i] = cty.StringVal(name)
			}
			return cty.TupleVal(keys).WithMarks(marks), nil
		default:
			if !m.IsKnown() {
				return cty.UnknownVal(retType).WithMarks(marks), nil
			}

			for it := m.ElementIterator(); it.Next(); {
				k, _ := it.Element()
				keys = append(keys, k)
			}
			if len(keys) == 0 {
				return cty.ListValEmpty(cty.String).WithMarks(marks), nil
			}
			return cty.ListVal(keys).WithMarks(marks), nil
		}
	},
})

KeysFunc is a function that takes a map and returns a sorted list of the map keys.

View Source
var LengthFunc = function.New(&function.Spec{
	Description: `Returns the number of elements in the given collection.`,
	Params: []function.Parameter{
		{
			Name:             "collection",
			Type:             cty.DynamicPseudoType,
			AllowDynamicType: true,
			AllowUnknown:     true,
			AllowMarked:      true,
		},
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		collTy := args[0].Type()
		if !(collTy.IsTupleType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType) {
			return cty.NilType, fmt.Errorf("collection must be a list, a map or a tuple")
		}
		return cty.Number, nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].Length(), nil
	},
})
View Source
var LessThanFunc = function.New(&function.Spec{
	Description: `Returns true if and only if the second number is less than the first.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].LessThan(args[1]), nil
	},
})
View Source
var LessThanOrEqualToFunc = function.New(&function.Spec{
	Description: `Returns true if and only if the second number is less than or equal to the first.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].LessThanOrEqualTo(args[1]), nil
	},
})
View Source
var LogFunc = function.New(&function.Spec{
	Description: `Returns the logarithm of the given number in the given base.`,
	Params: []function.Parameter{
		{
			Name: "num",
			Type: cty.Number,
		},
		{
			Name: "base",
			Type: cty.Number,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		var num float64
		if err := gocty.FromCtyValue(args[0], &num); err != nil {
			return cty.UnknownVal(cty.String), err
		}

		var base float64
		if err := gocty.FromCtyValue(args[1], &base); err != nil {
			return cty.UnknownVal(cty.String), err
		}

		return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil
	},
})

LogFunc is a function that returns the logarithm of a given number in a given base.

View Source
var LookupFunc = function.New(&function.Spec{
	Description: `Returns the value of the element with the given key from the given map, or returns the default value if there is no such element.`,
	Params: []function.Parameter{
		{
			Name:        "inputMap",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
		{
			Name:        "key",
			Type:        cty.String,
			AllowMarked: true,
		},
		{
			Name:        "default",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		ty := args[0].Type()

		switch {
		case ty.IsObjectType():
			if !args[1].IsKnown() {
				return cty.DynamicPseudoType, nil
			}

			keyVal, _ := args[1].Unmark()
			key := keyVal.AsString()
			if ty.HasAttribute(key) {
				return args[0].GetAttr(key).Type(), nil
			} else if len(args) == 3 {

				return args[2].Type(), nil
			}
			return cty.DynamicPseudoType, function.NewArgErrorf(0, "the given object has no attribute %q", key)
		case ty.IsMapType():
			if len(args) == 3 {
				_, err = convert.Convert(args[2], ty.ElementType())
				if err != nil {
					return cty.NilType, function.NewArgErrorf(2, "the default value must have the same type as the map elements")
				}
			}
			return ty.ElementType(), nil
		default:
			return cty.NilType, function.NewArgErrorf(0, "lookup() requires a map as the first argument")
		}
	},
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {

		defaultVal := args[2]

		var markses []cty.ValueMarks

		mapVar, mapMarks := args[0].Unmark()
		markses = append(markses, mapMarks)

		keyVal, keyMarks := args[1].Unmark()
		if len(keyMarks) > 0 {
			markses = append(markses, keyMarks)
		}
		lookupKey := keyVal.AsString()

		if !mapVar.IsWhollyKnown() {
			return cty.UnknownVal(retType).WithMarks(markses...), nil
		}

		if mapVar.Type().IsObjectType() {
			if mapVar.Type().HasAttribute(lookupKey) {
				return mapVar.GetAttr(lookupKey).WithMarks(markses...), nil
			}
		} else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True {
			return mapVar.Index(cty.StringVal(lookupKey)).WithMarks(markses...), nil
		}

		defaultVal, err = convert.Convert(defaultVal, retType)
		if err != nil {
			return cty.NilVal, err
		}
		return defaultVal.WithMarks(markses...), nil
	},
})

LookupFunc is a function that performs dynamic lookups of map types.

View Source
var LowerFunc = function.New(&function.Spec{
	Description: "Returns the given string with all Unicode letters translated to their lowercase equivalents.",
	Params: []function.Parameter{
		{
			Name:             "str",
			Type:             cty.String,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		in := args[0].AsString()
		out := strings.ToLower(in)
		return cty.StringVal(out), nil
	},
})
View Source
var MaxFunc = function.New(&function.Spec{
	Description: `Returns the numerically greatest of all of the given numbers.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:             "numbers",
		Type:             cty.Number,
		AllowDynamicType: true,
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		if len(args) == 0 {
			return cty.NilVal, fmt.Errorf("must pass at least one number")
		}

		max := cty.NegativeInfinity
		for _, num := range args {
			if num.GreaterThan(max).True() {
				max = num
			}
		}

		return max, nil
	},
})
View Source
var MergeFunc = function.New(&function.Spec{
	Description: `Merges all of the elements from the given maps into a single map, or the attributes from given objects into a single object.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:             "maps",
		Type:             cty.DynamicPseudoType,
		AllowDynamicType: true,
		AllowNull:        true,
		AllowMarked:      true,
	},
	Type: func(args []cty.Value) (cty.Type, error) {

		if len(args) == 0 {
			return cty.EmptyObject, nil
		}

		attrs := map[string]cty.Type{}

		first := cty.NilType
		matching := true
		attrsKnown := true
		for i, arg := range args {
			ty := arg.Type()

			if ty.Equals(cty.DynamicPseudoType) {
				return cty.DynamicPseudoType, nil
			}

			if !ty.IsMapType() && !ty.IsObjectType() {
				return cty.NilType, fmt.Errorf("arguments must be maps or objects, got %#v", ty.FriendlyName())
			}

			arg, _ = arg.Unmark()

			switch {
			case ty.IsObjectType() && !arg.IsNull():
				for attr, aty := range ty.AttributeTypes() {
					attrs[attr] = aty
				}
			case ty.IsMapType():
				switch {
				case arg.IsNull():

				case arg.IsKnown():
					ety := arg.Type().ElementType()
					for it := arg.ElementIterator(); it.Next(); {
						attr, _ := it.Element()
						attrs[attr.AsString()] = ety
					}
				default:

					attrsKnown = false
				}
			}

			if i == 0 {
				first = arg.Type()
				continue
			}

			if !ty.Equals(first) && matching {
				matching = false
			}
		}

		if matching {
			return first, nil
		}

		if !attrsKnown {
			return cty.DynamicPseudoType, nil
		}

		return cty.Object(attrs), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		outputMap := make(map[string]cty.Value)
		var markses []cty.ValueMarks // remember any marked maps/objects we find

		for _, arg := range args {
			if arg.IsNull() {
				continue
			}
			arg, argMarks := arg.Unmark()
			if len(argMarks) > 0 {
				markses = append(markses, argMarks)
			}
			for it := arg.ElementIterator(); it.Next(); {
				k, v := it.Element()
				outputMap[k.AsString()] = v
			}
		}

		switch {
		case retType.IsMapType():
			if len(outputMap) == 0 {
				return cty.MapValEmpty(retType.ElementType()).WithMarks(markses...), nil
			}
			return cty.MapVal(outputMap).WithMarks(markses...), nil
		case retType.IsObjectType(), retType.Equals(cty.DynamicPseudoType):
			return cty.ObjectVal(outputMap).WithMarks(markses...), nil
		default:
			panic(fmt.Sprintf("unexpected return type: %#v", retType))
		}
	},
})

MergeFunc constructs a function that takes an arbitrary number of maps or objects, and returns a single value that contains a merged set of keys and values from all of the inputs.

If more than one given map or object defines the same key then the one that is later in the argument sequence takes precedence.

View Source
var MinFunc = function.New(&function.Spec{
	Description: `Returns the numerically smallest of all of the given numbers.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:             "numbers",
		Type:             cty.Number,
		AllowDynamicType: true,
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		if len(args) == 0 {
			return cty.NilVal, fmt.Errorf("must pass at least one number")
		}

		min := cty.PositiveInfinity
		for _, num := range args {
			if num.LessThan(min).True() {
				min = num
			}
		}

		return min, nil
	},
})
View Source
var ModuloFunc = function.New(&function.Spec{
	Description: `Divides the first given number by the second and then returns the remainder.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {

		defer func() {
			if r := recover(); r != nil {
				if _, ok := r.(big.ErrNaN); ok {
					ret = cty.NilVal
					err = fmt.Errorf("can't use modulo with zero and infinity")
				} else {

					panic(r)
				}
			}
		}()

		return args[0].Modulo(args[1]), nil
	},
})
View Source
var MultiplyFunc = function.New(&function.Spec{
	Description: `Returns the product of the two given numbers.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {

		defer func() {
			if r := recover(); r != nil {
				if _, ok := r.(big.ErrNaN); ok {
					ret = cty.NilVal
					err = fmt.Errorf("can't multiply zero by infinity")
				} else {

					panic(r)
				}
			}
		}()

		return args[0].Multiply(args[1]), nil
	},
})
View Source
var NegateFunc = function.New(&function.Spec{
	Description: `Multiplies the given number by -1.`,
	Params: []function.Parameter{
		{
			Name:             "num",
			Type:             cty.Number,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return args[0].Negate(), nil
	},
})
View Source
var NotEqualFunc = function.New(&function.Spec{
	Description: `Returns false if the two given values are equal, or true otherwise.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.DynamicPseudoType,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowNull:        true,
		},
		{
			Name:             "b",
			Type:             cty.DynamicPseudoType,
			AllowUnknown:     true,
			AllowDynamicType: true,
			AllowNull:        true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].Equals(args[1]).Not(), nil
	},
})
View Source
var NotFunc = function.New(&function.Spec{
	Description: `Applies the logical NOT operation to the given boolean value.`,
	Params: []function.Parameter{
		{
			Name:             "val",
			Type:             cty.Bool,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return args[0].Not(), nil
	},
})
View Source
var OrFunc = function.New(&function.Spec{
	Description: `Applies the logical OR operation to the given boolean values.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Bool,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
		{
			Name:             "b",
			Type:             cty.Bool,
			AllowDynamicType: true,
			AllowMarked:      true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return args[0].Or(args[1]), nil
	},
})
View Source
var ParseIntFunc = function.New(&function.Spec{
	Description: `Parses the given string as a number of the given base, or raises an error if the string contains invalid characters.`,
	Params: []function.Parameter{
		{
			Name: "number",
			Type: cty.DynamicPseudoType,
		},
		{
			Name: "base",
			Type: cty.Number,
		},
	},

	Type: func(args []cty.Value) (cty.Type, error) {
		if !args[0].Type().Equals(cty.String) {
			return cty.Number, function.NewArgErrorf(0, "first argument must be a string, not %s", args[0].Type().FriendlyName())
		}
		return cty.Number, nil
	},
	RefineResult: refineNonNull,

	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		var numstr string
		var base int
		var err error

		if err = gocty.FromCtyValue(args[0], &numstr); err != nil {
			return cty.UnknownVal(cty.String), function.NewArgError(0, err)
		}

		if err = gocty.FromCtyValue(args[1], &base); err != nil {
			return cty.UnknownVal(cty.Number), function.NewArgError(1, err)
		}

		if base < 2 || base > 62 {
			return cty.UnknownVal(cty.Number), function.NewArgErrorf(
				1,
				"base must be a whole number between 2 and 62 inclusive",
			)
		}

		num, ok := (&big.Int{}).SetString(numstr, base)
		if !ok {
			return cty.UnknownVal(cty.Number), function.NewArgErrorf(
				0,
				"cannot parse %q as a base %d integer",
				numstr,
				base,
			)
		}

		parsedNum := cty.NumberVal((&big.Float{}).SetInt(num))

		return parsedNum, nil
	},
})

ParseIntFunc is a function that parses a string argument and returns an integer of the specified base.

View Source
var PowFunc = function.New(&function.Spec{
	Description: `Returns the given number raised to the given power (exponentiation).`,
	Params: []function.Parameter{
		{
			Name: "num",
			Type: cty.Number,
		},
		{
			Name: "power",
			Type: cty.Number,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		var num float64
		if err := gocty.FromCtyValue(args[0], &num); err != nil {
			return cty.UnknownVal(cty.String), err
		}

		var power float64
		if err := gocty.FromCtyValue(args[1], &power); err != nil {
			return cty.UnknownVal(cty.String), err
		}

		return cty.NumberFloatVal(math.Pow(num, power)), nil
	},
})

PowFunc is a function that returns the logarithm of a given number in a given base.

View Source
var RangeFunc = function.New(&function.Spec{
	Description: `Returns a list of numbers spread evenly over a particular range.`,
	VarParam: &function.Parameter{
		Name: "params",
		Type: cty.Number,
	},
	Type:         function.StaticReturnType(cty.List(cty.Number)),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		var start, end, step cty.Value
		switch len(args) {
		case 1:
			if args[0].LessThan(cty.Zero).True() {
				start, end, step = cty.Zero, args[0], cty.NumberIntVal(-1)
			} else {
				start, end, step = cty.Zero, args[0], cty.NumberIntVal(1)
			}
		case 2:
			if args[1].LessThan(args[0]).True() {
				start, end, step = args[0], args[1], cty.NumberIntVal(-1)
			} else {
				start, end, step = args[0], args[1], cty.NumberIntVal(1)
			}
		case 3:
			start, end, step = args[0], args[1], args[2]
		default:
			return cty.NilVal, fmt.Errorf("must have one, two, or three arguments")
		}

		var vals []cty.Value

		if step == cty.Zero {
			return cty.NilVal, function.NewArgErrorf(2, "step must not be zero")
		}
		down := step.LessThan(cty.Zero).True()

		if down {
			if end.GreaterThan(start).True() {
				return cty.NilVal, function.NewArgErrorf(1, "end must be less than start when step is negative")
			}
		} else {
			if end.LessThan(start).True() {
				return cty.NilVal, function.NewArgErrorf(1, "end must be greater than start when step is positive")
			}
		}

		num := start
		for {
			if down {
				if num.LessThanOrEqualTo(end).True() {
					break
				}
			} else {
				if num.GreaterThanOrEqualTo(end).True() {
					break
				}
			}
			if len(vals) >= 1024 {

				return cty.NilVal, fmt.Errorf("more than 1024 values were generated; either decrease the difference between start and end or use a smaller step")
			}
			vals = append(vals, num)
			num = num.Add(step)
		}
		if len(vals) == 0 {
			return cty.ListValEmpty(cty.Number), nil
		}
		return cty.ListVal(vals), nil
	},
})
View Source
var RegexAllFunc = function.New(&function.Spec{
	Description: `Applies the given regular expression pattern to the given string and returns a list of information about all non-overlapping matches, or an empty list if there are no matches.`,
	Params: []function.Parameter{
		{
			Name: "pattern",
			Type: cty.String,
		},
		{
			Name: "string",
			Type: cty.String,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		if !args[0].IsKnown() {

			return cty.List(cty.DynamicPseudoType), nil
		}

		retTy, err := regexPatternResultType(args[0].AsString())
		if err != nil {
			err = function.NewArgError(0, err)
		}
		return cty.List(retTy), err
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		ety := retType.ElementType()
		if ety == cty.DynamicPseudoType {
			return cty.DynamicVal, nil
		}

		re, err := regexp.Compile(args[0].AsString())
		if err != nil {

			return cty.NilVal, function.NewArgErrorf(0, "error parsing pattern: %s", err)
		}
		str := args[1].AsString()

		captureIdxsEach := re.FindAllStringSubmatchIndex(str, -1)
		if len(captureIdxsEach) == 0 {
			return cty.ListValEmpty(ety), nil
		}

		elems := make([]cty.Value, len(captureIdxsEach))
		for i, captureIdxs := range captureIdxsEach {
			elems[i] = regexPatternResult(re, str, captureIdxs, ety)
		}
		return cty.ListVal(elems), nil
	},
})
View Source
var RegexFunc = function.New(&function.Spec{
	Description: `Applies the given regular expression pattern to the given string and returns information about a single match, or raises an error if there is no match.`,
	Params: []function.Parameter{
		{
			Name: "pattern",
			Type: cty.String,
		},
		{
			Name: "string",
			Type: cty.String,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		if !args[0].IsKnown() {

			return cty.DynamicPseudoType, nil
		}

		retTy, err := regexPatternResultType(args[0].AsString())
		if err != nil {
			err = function.NewArgError(0, err)
		}
		return retTy, err
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		if retType == cty.DynamicPseudoType {
			return cty.DynamicVal, nil
		}

		re, err := regexp.Compile(args[0].AsString())
		if err != nil {

			return cty.NilVal, function.NewArgErrorf(0, "error parsing pattern: %s", err)
		}
		str := args[1].AsString()

		captureIdxs := re.FindStringSubmatchIndex(str)
		if captureIdxs == nil {
			return cty.NilVal, fmt.Errorf("pattern did not match any part of the given string")
		}

		return regexPatternResult(re, str, captureIdxs, retType), nil
	},
})
View Source
var RegexReplaceFunc = function.New(&function.Spec{
	Description: `Applies the given regular expression pattern to the given string and replaces all matches with the given replacement string.`,
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
		{
			Name: "pattern",
			Type: cty.String,
		},
		{
			Name: "replace",
			Type: cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		str := args[0].AsString()
		substr := args[1].AsString()
		replace := args[2].AsString()

		re, err := regexp.Compile(substr)
		if err != nil {
			return cty.UnknownVal(cty.String), err
		}

		return cty.StringVal(re.ReplaceAllString(str, replace)), nil
	},
})

RegexReplaceFunc is a function that searches a given string for another given substring, and replaces each occurence with a given replacement string. The substr argument must be a valid regular expression.

View Source
var ReplaceFunc = function.New(&function.Spec{
	Description: `Replaces all instances of the given substring in the given string with the given replacement string.`,
	Params: []function.Parameter{
		{
			Name:        "str",
			Description: `The string to search within.`,
			Type:        cty.String,
		},
		{
			Name:        "substr",
			Description: `The substring to search for.`,
			Type:        cty.String,
		},
		{
			Name:        "replace",
			Description: `The new substring to replace substr with.`,
			Type:        cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		str := args[0].AsString()
		substr := args[1].AsString()
		replace := args[2].AsString()

		return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil
	},
})

ReplaceFunc is a function that searches a given string for another given substring, and replaces each occurence with a given replacement string. The substr argument is a simple string.

View Source
var ReverseFunc = function.New(&function.Spec{
	Description: "Returns the given string with all of its Unicode characters in reverse order.",
	Params: []function.Parameter{
		{
			Name:             "str",
			Type:             cty.String,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		in := []byte(args[0].AsString())
		out := make([]byte, len(in))
		pos := len(out)

		inB := []byte(in)
		for i := 0; i < len(in); {
			d, _, _ := textseg.ScanGraphemeClusters(inB[i:], true)
			cluster := in[i : i+d]
			pos -= len(cluster)
			copy(out[pos:], cluster)
			i += d
		}

		return cty.StringVal(string(out)), nil
	},
})
View Source
var ReverseListFunc = function.New(&function.Spec{
	Description: `Returns the given list with its elements in reverse order.`,
	Params: []function.Parameter{
		{
			Name:        "list",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		argTy := args[0].Type()
		switch {
		case argTy.IsTupleType():
			argTys := argTy.TupleElementTypes()
			retTys := make([]cty.Type, len(argTys))
			for i, ty := range argTys {
				retTys[len(retTys)-i-1] = ty
			}
			return cty.Tuple(retTys), nil
		case argTy.IsListType(), argTy.IsSetType():
			return cty.List(argTy.ElementType()), nil
		default:
			return cty.NilType, function.NewArgErrorf(0, "can only reverse list or tuple values, not %s", argTy.FriendlyName())
		}
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		in, marks := args[0].Unmark()
		inVals := in.AsValueSlice()
		outVals := make([]cty.Value, len(inVals))

		for i, v := range inVals {
			outVals[len(outVals)-i-1] = v
		}
		switch {
		case retType.IsTupleType():
			return cty.TupleVal(outVals).WithMarks(marks), nil
		default:
			if len(outVals) == 0 {
				return cty.ListValEmpty(retType.ElementType()).WithMarks(marks), nil
			}
			return cty.ListVal(outVals).WithMarks(marks), nil
		}
	},
})

ReverseListFunc takes a sequence and produces a new sequence of the same length with all of the same elements as the given sequence but in reverse order.

View Source
var SetHasElementFunc = function.New(&function.Spec{
	Description: `Returns true if the given set contains the given element, or false otherwise.`,
	Params: []function.Parameter{
		{
			Name:             "set",
			Type:             cty.Set(cty.DynamicPseudoType),
			AllowDynamicType: true,
		},
		{
			Name:             "elem",
			Type:             cty.DynamicPseudoType,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Bool),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return args[0].HasElement(args[1]), nil
	},
})
View Source
var SetIntersectionFunc = function.New(&function.Spec{
	Description: `Returns the intersection of all given sets.`,
	Params: []function.Parameter{
		{
			Name:             "first_set",
			Type:             cty.Set(cty.DynamicPseudoType),
			AllowDynamicType: true,
		},
	},
	VarParam: &function.Parameter{
		Name:             "other_sets",
		Type:             cty.Set(cty.DynamicPseudoType),
		AllowDynamicType: true,
	},
	Type:         setOperationReturnType,
	RefineResult: refineNonNull,
	Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
		return s1.Intersection(s2)
	}, false),
})
View Source
var SetProductFunc = function.New(&function.Spec{
	Description: `Calculates the cartesian product of two or more sets.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:         "sets",
		Description:  "The sets to consider. Also accepts lists and tuples, and if all arguments are of list or tuple type then the result will preserve the input ordering",
		Type:         cty.DynamicPseudoType,
		AllowMarked:  true,
		AllowUnknown: true,
	},
	Type: func(args []cty.Value) (retType cty.Type, err error) {
		if len(args) < 2 {
			return cty.NilType, errors.New("at least two arguments are required")
		}

		listCount := 0
		elemTys := make([]cty.Type, len(args))
		for i, arg := range args {
			aty := arg.Type()
			switch {
			case aty.IsSetType():
				elemTys[i] = aty.ElementType()
			case aty.IsListType():
				elemTys[i] = aty.ElementType()
				listCount++
			case aty.IsTupleType():

				allEtys := aty.TupleElementTypes()
				if len(allEtys) == 0 {
					elemTys[i] = cty.DynamicPseudoType
					listCount++
					break
				}
				ety, _ := convert.UnifyUnsafe(allEtys)
				if ety == cty.NilType {
					return cty.NilType, function.NewArgErrorf(i, "all elements must be of the same type")
				}
				elemTys[i] = ety
				listCount++
			default:
				return cty.NilType, function.NewArgErrorf(i, "a set or a list is required")
			}
		}

		if listCount == len(args) {
			return cty.List(cty.Tuple(elemTys)), nil
		}
		return cty.Set(cty.Tuple(elemTys)), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		ety := retType.ElementType()
		var retMarks cty.ValueMarks

		total := 1
		var hasUnknownLength bool
		for _, arg := range args {
			arg, marks := arg.Unmark()
			retMarks = cty.NewValueMarks(retMarks, marks)

			if !(arg.IsKnown() && arg.Length().IsKnown()) {
				hasUnknownLength = true
				continue
			}

			total *= arg.LengthInt()
		}

		if hasUnknownLength {
			defer func() {

				ret = ret.WithMarks(retMarks)
			}()
			ret := cty.UnknownVal(retType)

			maxLength := 1
			for _, arg := range args {
				arg, _ := arg.Unmark()
				argRng := arg.Range()
				ty := argRng.TypeConstraint()
				var argMaxLen int
				if ty.IsCollectionType() {
					argMaxLen = argRng.LengthUpperBound()
				} else if ty.IsTupleType() {
					argMaxLen = ty.Length()
				} else {

					return ret, nil
				}

				if argMaxLen > 1024 {
					return ret, nil
				}
				maxLength *= argMaxLen
				if maxLength > 2048 {
					return ret, nil
				}
				if maxLength < 0 {
					return ret, nil
				}
			}

			if maxLength == 0 {

				ret = ret.Refine().CollectionLength(0).NewValue()
			} else {

				ret = ret.Refine().
					CollectionLengthLowerBound(1).
					CollectionLengthUpperBound(maxLength).
					NewValue()
			}
			return ret, nil
		}

		if total == 0 {

			if retType.IsListType() {
				return cty.ListValEmpty(ety).WithMarks(retMarks), nil
			}
			return cty.SetValEmpty(ety).WithMarks(retMarks), nil
		}

		subEtys := ety.TupleElementTypes()
		product := make([][]cty.Value, total)

		b := make([]cty.Value, total*len(args))
		n := make([]int, len(args))
		s := 0
		argVals := make([][]cty.Value, len(args))
		for i, arg := range args {

			arg, _ := arg.Unmark()
			argVals[i] = arg.AsValueSlice()
		}

		for i := range product {
			e := s + len(args)
			pi := b[s:e]
			product[i] = pi
			s = e

			for j, n := range n {
				val := argVals[j][n]
				ty := subEtys[j]
				if !val.Type().Equals(ty) {
					var err error
					val, err = convert.Convert(val, ty)
					if err != nil {

						return cty.NilVal, fmt.Errorf("failed to convert argVals[%d][%d] to %s; this is a bug in cty", j, n, ty.FriendlyName())
					}
				}
				pi[j] = val
			}

			for j := len(n) - 1; j >= 0; j-- {
				n[j]++
				if n[j] < len(argVals[j]) {
					break
				}
				n[j] = 0
			}
		}

		productVals := make([]cty.Value, total)
		for i, vals := range product {
			productVals[i] = cty.TupleVal(vals)
		}

		if retType.IsListType() {
			return cty.ListVal(productVals).WithMarks(retMarks), nil
		}
		return cty.SetVal(productVals).WithMarks(retMarks), nil
	},
})

SetProductFunc calculates the Cartesian product of two or more sets or sequences. If the arguments are all lists then the result is a list of tuples, preserving the ordering of all of the input lists. Otherwise the result is a set of tuples.

View Source
var SetSubtractFunc = function.New(&function.Spec{
	Description: `Returns the relative complement of the two given sets.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Set(cty.DynamicPseudoType),
			AllowDynamicType: true,
		},
		{
			Name:             "b",
			Type:             cty.Set(cty.DynamicPseudoType),
			AllowDynamicType: true,
		},
	},
	Type:         setOperationReturnType,
	RefineResult: refineNonNull,
	Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
		return s1.Subtract(s2)
	}, false),
})
View Source
var SetSymmetricDifferenceFunc = function.New(&function.Spec{
	Description: `Returns the symmetric difference of the two given sets.`,
	Params: []function.Parameter{
		{
			Name:             "first_set",
			Type:             cty.Set(cty.DynamicPseudoType),
			AllowDynamicType: true,
		},
	},
	VarParam: &function.Parameter{
		Name:             "other_sets",
		Type:             cty.Set(cty.DynamicPseudoType),
		AllowDynamicType: true,
	},
	Type:         setOperationReturnType,
	RefineResult: refineNonNull,
	Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
		return s1.SymmetricDifference(s2)
	}, false),
})
View Source
var SetUnionFunc = function.New(&function.Spec{
	Description: `Returns the union of all given sets.`,
	Params: []function.Parameter{
		{
			Name:             "first_set",
			Type:             cty.Set(cty.DynamicPseudoType),
			AllowDynamicType: true,
		},
	},
	VarParam: &function.Parameter{
		Name:             "other_sets",
		Type:             cty.Set(cty.DynamicPseudoType),
		AllowDynamicType: true,
	},
	Type:         setOperationReturnType,
	RefineResult: refineNonNull,
	Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
		return s1.Union(s2)
	}, true),
})
View Source
var SignumFunc = function.New(&function.Spec{
	Description: `Returns 0 if the given number is zero, 1 if the given number is positive, or -1 if the given number is negative.`,
	Params: []function.Parameter{
		{
			Name: "num",
			Type: cty.Number,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		var num int
		if err := gocty.FromCtyValue(args[0], &num); err != nil {
			return cty.UnknownVal(cty.String), err
		}
		switch {
		case num < 0:
			return cty.NumberIntVal(-1), nil
		case num > 0:
			return cty.NumberIntVal(+1), nil
		default:
			return cty.NumberIntVal(0), nil
		}
	},
})

SignumFunc is a function that determines the sign of a number, returning a number between -1 and 1 to represent the sign..

View Source
var SliceFunc = function.New(&function.Spec{
	Description: `Extracts a subslice of the given list or tuple value.`,
	Params: []function.Parameter{
		{
			Name:        "list",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
		{
			Name: "start_index",
			Type: cty.Number,
		},
		{
			Name: "end_index",
			Type: cty.Number,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		arg := args[0]
		argTy := arg.Type()

		if argTy.IsSetType() {
			return cty.NilType, function.NewArgErrorf(0, "cannot slice a set, because its elements do not have indices; explicitly convert to a list if the ordering of the result is not important")
		}
		if !argTy.IsListType() && !argTy.IsTupleType() {
			return cty.NilType, function.NewArgErrorf(0, "must be a list or tuple value")
		}

		startIndex, endIndex, idxsKnown, err := sliceIndexes(args)
		if err != nil {
			return cty.NilType, err
		}

		if argTy.IsListType() {
			return argTy, nil
		}

		if !idxsKnown {

			return cty.DynamicPseudoType, nil
		}
		return cty.Tuple(argTy.TupleElementTypes()[startIndex:endIndex]), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		inputList, marks := args[0].Unmark()

		if retType == cty.DynamicPseudoType {
			return cty.DynamicVal.WithMarks(marks), nil
		}

		startIndex, endIndex, _, err := sliceIndexes(args)
		if err != nil {
			return cty.NilVal, err
		}

		if endIndex-startIndex == 0 {
			if retType.IsTupleType() {
				return cty.EmptyTupleVal.WithMarks(marks), nil
			}
			return cty.ListValEmpty(retType.ElementType()).WithMarks(marks), nil
		}

		outputList := inputList.AsValueSlice()[startIndex:endIndex]

		if retType.IsTupleType() {
			return cty.TupleVal(outputList).WithMarks(marks), nil
		}

		return cty.ListVal(outputList).WithMarks(marks), nil
	},
})

SliceFunc is a function that extracts some consecutive elements from within a list.

View Source
var SortFunc = function.New(&function.Spec{
	Description: "Applies a lexicographic sort to the elements of the given list.",
	Params: []function.Parameter{
		{
			Name:         "list",
			Type:         cty.List(cty.String),
			AllowUnknown: true,
		},
	},
	Type:         function.StaticReturnType(cty.List(cty.String)),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		listVal := args[0]

		if !listVal.IsWhollyKnown() {

			ret := cty.UnknownVal(retType)
			if listVal.Type().IsListType() {
				rng := listVal.Range()
				ret = ret.Refine().
					CollectionLengthLowerBound(rng.LengthLowerBound()).
					CollectionLengthUpperBound(rng.LengthUpperBound()).
					NewValue()
			}
			return ret, nil
		}
		if listVal.LengthInt() == 0 {
			return listVal, nil
		}

		list := make([]string, 0, listVal.LengthInt())
		for it := listVal.ElementIterator(); it.Next(); {
			iv, v := it.Element()
			if v.IsNull() {
				return cty.UnknownVal(retType), fmt.Errorf("given list element %s is null; a null string cannot be sorted", iv.AsBigFloat().String())
			}
			list = append(list, v.AsString())
		}

		sort.Strings(list)
		retVals := make([]cty.Value, len(list))
		for i, s := range list {
			retVals[i] = cty.StringVal(s)
		}
		return cty.ListVal(retVals), nil
	},
})
View Source
var SplitFunc = function.New(&function.Spec{
	Description: "Produces a list of one or more strings by splitting the given string at all instances of a given separator substring.",
	Params: []function.Parameter{
		{
			Name:        "separator",
			Description: "The substring that delimits the result strings.",
			Type:        cty.String,
		},
		{
			Name:        "str",
			Description: "The string to split.",
			Type:        cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.List(cty.String)),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		sep := args[0].AsString()
		str := args[1].AsString()
		elems := strings.Split(str, sep)
		elemVals := make([]cty.Value, len(elems))
		for i, s := range elems {
			elemVals[i] = cty.StringVal(s)
		}
		if len(elemVals) == 0 {
			return cty.ListValEmpty(cty.String), nil
		}
		return cty.ListVal(elemVals), nil
	},
})
View Source
var StrlenFunc = function.New(&function.Spec{
	Description: "Returns the number of Unicode characters (technically: grapheme clusters) in the given string.",
	Params: []function.Parameter{
		{
			Name:             "str",
			Type:             cty.String,
			AllowUnknown:     true,
			AllowDynamicType: true,
		},
	},
	Type: function.StaticReturnType(cty.Number),
	RefineResult: func(b *cty.RefinementBuilder) *cty.RefinementBuilder {

		return b.NotNull().NumberRangeLowerBound(cty.NumberIntVal(0), true)
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		if !args[0].IsKnown() {
			ret := cty.UnknownVal(cty.Number)

			inRng := args[0].Range()
			if inRng.TypeConstraint() == cty.String {
				prefixLen := int64(graphemeClusterCount(inRng.StringPrefix()))
				ret = ret.Refine().NumberRangeLowerBound(cty.NumberIntVal(prefixLen), true).NewValue()
			}
			return ret, nil
		}

		in := args[0].AsString()
		l := graphemeClusterCount(in)
		return cty.NumberIntVal(int64(l)), nil
	},
})
View Source
var SubstrFunc = function.New(&function.Spec{
	Description: "Extracts a substring from the given string.",
	Params: []function.Parameter{
		{
			Name:             "str",
			Description:      "The input string.",
			Type:             cty.String,
			AllowDynamicType: true,
		},
		{
			Name:             "offset",
			Description:      "The starting offset in Unicode characters.",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
		{
			Name:             "length",
			Description:      "The maximum length of the result in Unicode characters.",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		in := []byte(args[0].AsString())
		var offset, length int

		var err error
		err = gocty.FromCtyValue(args[1], &offset)
		if err != nil {
			return cty.NilVal, err
		}
		err = gocty.FromCtyValue(args[2], &length)
		if err != nil {
			return cty.NilVal, err
		}

		if offset < 0 {
			totalLenNum, err := Strlen(args[0])
			if err != nil {

				panic("Stdlen returned an error")
			}

			var totalLen int
			err = gocty.FromCtyValue(totalLenNum, &totalLen)
			if err != nil {

				panic("Stdlen returned a non-int number")
			}

			offset += totalLen
		} else if length == 0 {

			return cty.StringVal(""), nil
		}

		sub := in
		pos := 0
		var i int

		if offset > 0 {
			for i = 0; i < len(sub); {
				d, _, _ := textseg.ScanGraphemeClusters(sub[i:], true)
				i += d
				pos++
				if pos == offset {
					break
				}
				if i >= len(in) {
					return cty.StringVal(""), nil
				}
			}

			sub = sub[i:]
		}

		if length < 0 {

			return cty.StringVal(string(sub)), nil
		}

		pos = 0
		for i = 0; i < len(sub); {
			d, _, _ := textseg.ScanGraphemeClusters(sub[i:], true)
			i += d
			pos++
			if pos == length {
				break
			}
		}

		sub = sub[:i]

		return cty.StringVal(string(sub)), nil
	},
})
View Source
var SubtractFunc = function.New(&function.Spec{
	Description: `Returns the difference between the two given numbers.`,
	Params: []function.Parameter{
		{
			Name:             "a",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
		{
			Name:             "b",
			Type:             cty.Number,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.Number),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {

		defer func() {
			if r := recover(); r != nil {
				if _, ok := r.(big.ErrNaN); ok {
					ret = cty.NilVal
					err = fmt.Errorf("can't subtract infinity from itself")
				} else {

					panic(r)
				}
			}
		}()
		return args[0].Subtract(args[1]), nil
	},
})
View Source
var TimeAddFunc = function.New(&function.Spec{
	Description: `Adds the duration represented by the given duration string to the given RFC 3339 timestamp string, returning another RFC 3339 timestamp.`,
	Params: []function.Parameter{
		{
			Name: "timestamp",
			Type: cty.String,
		},
		{
			Name: "duration",
			Type: cty.String,
		},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		ts, err := parseTimestamp(args[0].AsString())
		if err != nil {
			return cty.UnknownVal(cty.String), err
		}
		duration, err := time.ParseDuration(args[1].AsString())
		if err != nil {
			return cty.UnknownVal(cty.String), err
		}

		return cty.StringVal(ts.Add(duration).Format(time.RFC3339)), nil
	},
})

TimeAddFunc is a function that adds a duration to a timestamp, returning a new timestamp.

View Source
var TitleFunc = function.New(&function.Spec{
	Description: "Replaces one letter after each non-letter and non-digit character with its uppercase equivalent.",
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return cty.StringVal(strings.Title(args[0].AsString())), nil
	},
})

TitleFunc is a function that converts the first letter of each word in the given string to uppercase.

View Source
var TrimFunc = function.New(&function.Spec{
	Description: "Removes consecutive sequences of characters in \"cutset\" from the start and end of the given string.",
	Params: []function.Parameter{
		{
			Name:        "str",
			Description: "The string to trim.",
			Type:        cty.String,
		},
		{
			Name:        "cutset",
			Description: "A string containing all of the characters to trim. Each character is taken separately, so the order of characters is insignificant.",
			Type:        cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		str := args[0].AsString()
		cutset := args[1].AsString()

		return cty.StringVal(strings.Trim(str, cutset)), nil
	},
})

TrimFunc is a function that removes the specified characters from the start and end of the given string.

View Source
var TrimPrefixFunc = function.New(&function.Spec{
	Description: "Removes the given prefix from the start of the given string, if present.",
	Params: []function.Parameter{
		{
			Name:        "str",
			Description: "The string to trim.",
			Type:        cty.String,
		},
		{
			Name:        "prefix",
			Description: "The prefix to remove, if present.",
			Type:        cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		str := args[0].AsString()
		prefix := args[1].AsString()
		return cty.StringVal(strings.TrimPrefix(str, prefix)), nil
	},
})

TrimPrefixFunc is a function that removes the specified characters from the start the given string.

View Source
var TrimSpaceFunc = function.New(&function.Spec{
	Description: "Removes any consecutive space characters (as defined by Unicode) from the start and end of the given string.",
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		return cty.StringVal(strings.TrimSpace(args[0].AsString())), nil
	},
})

TrimSpaceFunc is a function that removes any space characters from the start and end of the given string.

View Source
var TrimSuffixFunc = function.New(&function.Spec{
	Description: "Removes the given suffix from the start of the given string, if present.",
	Params: []function.Parameter{
		{
			Name:        "str",
			Description: "The string to trim.",
			Type:        cty.String,
		},
		{
			Name:        "suffix",
			Description: "The suffix to remove, if present.",
			Type:        cty.String,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		str := args[0].AsString()
		cutset := args[1].AsString()
		return cty.StringVal(strings.TrimSuffix(str, cutset)), nil
	},
})

TrimSuffixFunc is a function that removes the specified characters from the end of the given string.

View Source
var UpperFunc = function.New(&function.Spec{
	Description: "Returns the given string with all Unicode letters translated to their uppercase equivalents.",
	Params: []function.Parameter{
		{
			Name:             "str",
			Type:             cty.String,
			AllowDynamicType: true,
		},
	},
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		in := args[0].AsString()
		out := strings.ToUpper(in)
		return cty.StringVal(out), nil
	},
})
View Source
var ValuesFunc = function.New(&function.Spec{
	Description: `Returns the values of elements of a given map, or the values of attributes of a given object, in lexicographic order by key or attribute name.`,
	Params: []function.Parameter{
		{
			Name:        "mapping",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		ty := args[0].Type()
		if ty.IsMapType() {
			return cty.List(ty.ElementType()), nil
		} else if ty.IsObjectType() {

			atys := ty.AttributeTypes()
			if len(atys) == 0 {
				return cty.EmptyTuple, nil
			}
			attrNames := make([]string, 0, len(atys))
			for name := range atys {
				attrNames = append(attrNames, name)
			}
			sort.Strings(attrNames)

			tys := make([]cty.Type, len(attrNames))
			for i, name := range attrNames {
				tys[i] = atys[name]
			}
			return cty.Tuple(tys), nil
		}
		return cty.NilType, errors.New("values() requires a map as the first argument")
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		mapVar := args[0]

		mapVar, marks := mapVar.Unmark()

		// We can just iterate the map/object value here because cty guarantees
		// that these types always iterate in key lexicographical order.
		var values []cty.Value
		for it := mapVar.ElementIterator(); it.Next(); {
			_, val := it.Element()
			values = append(values, val)
		}

		if retType.IsTupleType() {
			return cty.TupleVal(values).WithMarks(marks), nil
		}
		if len(values) == 0 {
			return cty.ListValEmpty(retType.ElementType()).WithMarks(marks), nil
		}
		return cty.ListVal(values).WithMarks(marks), nil
	},
})

ValuesFunc is a function that returns a list of the map values, in the order of the sorted keys.

View Source
var ZipmapFunc = function.New(&function.Spec{
	Description: `Constructs a map from a list of keys and a corresponding list of values, which must both be of the same length.`,
	Params: []function.Parameter{
		{
			Name:        "keys",
			Type:        cty.List(cty.String),
			AllowMarked: true,
		},
		{
			Name:        "values",
			Type:        cty.DynamicPseudoType,
			AllowMarked: true,
		},
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		keys := args[0]
		values := args[1]
		valuesTy := values.Type()

		switch {
		case valuesTy.IsListType():
			return cty.Map(values.Type().ElementType()), nil
		case valuesTy.IsTupleType():
			if !keys.IsWhollyKnown() {

				return cty.DynamicPseudoType, nil
			}

			keys, _ := keys.Unmark()
			keysRaw := keys.AsValueSlice()
			valueTypesRaw := valuesTy.TupleElementTypes()
			if len(keysRaw) != len(valueTypesRaw) {
				return cty.NilType, fmt.Errorf("number of keys (%d) does not match number of values (%d)", len(keysRaw), len(valueTypesRaw))
			}
			atys := make(map[string]cty.Type, len(valueTypesRaw))
			for i, keyVal := range keysRaw {
				keyVal, _ = keyVal.Unmark()
				if keyVal.IsNull() {
					return cty.NilType, fmt.Errorf("keys list has null value at index %d", i)
				}
				key := keyVal.AsString()
				atys[key] = valueTypesRaw[i]
			}
			return cty.Object(atys), nil

		default:
			return cty.NilType, errors.New("values argument must be a list or tuple value")
		}
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		keys := args[0]
		values := args[1]
		keys, keysMarks := keys.Unmark()
		values, valuesMarks := values.Unmark()

		retMarks := cty.NewValueMarks(keysMarks, valuesMarks)

		if !keys.IsWhollyKnown() {

			return cty.UnknownVal(retType).WithMarks(retMarks), nil
		}

		if keys.LengthInt() != values.LengthInt() {
			return cty.NilVal, fmt.Errorf("number of keys (%d) does not match number of values (%d)", keys.LengthInt(), values.LengthInt())
		}

		output := make(map[string]cty.Value)

		i := 0
		for it := keys.ElementIterator(); it.Next(); {
			_, v := it.Element()
			v, vMarks := v.Unmark()
			val := values.Index(cty.NumberIntVal(int64(i)))
			output[v.AsString()] = val

			retMarks = cty.NewValueMarks(retMarks, vMarks)

			i++
		}

		switch {
		case retType.IsMapType():
			if len(output) == 0 {
				return cty.MapValEmpty(retType.ElementType()).WithMarks(retMarks), nil
			}
			return cty.MapVal(output).WithMarks(retMarks), nil
		case retType.IsObjectType():
			return cty.ObjectVal(output).WithMarks(retMarks), nil
		default:

			return cty.NilVal, fmt.Errorf("internally selected incorrect result type %s (this is a bug)", retType.FriendlyName())
		}
	},
})

ZipmapFunc is a function that constructs a map from a list of keys and a corresponding list of values.

Functions

func Absolute

func Absolute(num cty.Value) (cty.Value, error)

Absolute returns the magnitude of the given number, without its sign. That is, it turns negative values into positive values.

func Add

func Add(a cty.Value, b cty.Value) (cty.Value, error)

Add returns the sum of the two given numbers.

func And

func And(a, b cty.Value) (cty.Value, error)

And returns true if and only if both of the given boolean values are true.

func AssertNotNull added in v1.13.0

func AssertNotNull(v cty.Value) (cty.Value, error)

func BytesLen

func BytesLen(buf cty.Value) (cty.Value, error)

func BytesSlice

func BytesSlice(buf cty.Value, offset cty.Value, length cty.Value) (cty.Value, error)

func BytesVal

func BytesVal(buf []byte) cty.Value

BytesVal creates a new Bytes value from the given buffer, which must be non-nil or this function will panic.

Once a byte slice has been wrapped in a Bytes capsule, its underlying array must be considered immutable.

func CSVDecode

func CSVDecode(str cty.Value) (cty.Value, error)

CSVDecode parses the given CSV (RFC 4180) string and, if it is valid, returns a list of objects representing the rows.

The result is always a list of some object type. The first row of the input is used to determine the object attributes, and subsequent rows determine the values of those attributes.

func Ceil added in v1.3.0

func Ceil(num cty.Value) (cty.Value, error)

Ceil returns the closest whole number greater than or equal to the given value.

func Chomp added in v1.3.0

func Chomp(str cty.Value) (cty.Value, error)

Chomp removes newline characters at the end of a string.

func Chunklist added in v1.3.0

func Chunklist(list, size cty.Value) (cty.Value, error)

Chunklist splits a single list into fixed-size chunks, returning a list of lists.

func Coalesce

func Coalesce(vals ...cty.Value) (cty.Value, error)

Coalesce returns the first of the given arguments that is not null. If all arguments are null, an error is produced.

func CoalesceList added in v1.3.0

func CoalesceList(args ...cty.Value) (cty.Value, error)

CoalesceList takes any number of list arguments and returns the first one that isn't empty.

func Compact added in v1.3.0

func Compact(list cty.Value) (cty.Value, error)

Compact takes a list of strings and returns a new list with any empty string elements removed.

func Concat

func Concat(seqs ...cty.Value) (cty.Value, error)

Concat takes one or more sequences (lists or tuples) and returns the single sequence that results from concatenating them together in order.

If all of the given sequences are lists of the same element type then the result is a list of that type. Otherwise, the result is a of a tuple type constructed from the given sequence types.

func Contains added in v1.3.0

func Contains(list, value cty.Value) (cty.Value, error)

Contains determines whether a given list contains a given single value as one of its elements.

func Distinct added in v1.3.0

func Distinct(list cty.Value) (cty.Value, error)

Distinct takes a list and returns a new list with any duplicate elements removed.

func Divide

func Divide(a cty.Value, b cty.Value) (cty.Value, error)

Divide returns a divided by b, where both a and b are numbers.

func Element added in v1.3.0

func Element(list, index cty.Value) (cty.Value, error)

Element returns a single element from a given list at the given index. If index is greater than the length of the list then it is wrapped modulo the list length.

func Equal

func Equal(a cty.Value, b cty.Value) (cty.Value, error)

Equal determines whether the two given values are equal, returning a bool value.

func Flatten added in v1.3.0

func Flatten(list cty.Value) (cty.Value, error)

Flatten takes a list and replaces any elements that are lists with a flattened sequence of the list contents.

func Floor added in v1.3.0

func Floor(num cty.Value) (cty.Value, error)

Floor returns the closest whole number lesser than or equal to the given value.

func Format

func Format(format cty.Value, vals ...cty.Value) (cty.Value, error)

Format produces a string representation of zero or more values using a format string similar to the "printf" function in C.

It supports the following "verbs":

%%      Literal percent sign, consuming no value
%v      A default formatting of the value based on type, as described below.
%#v     JSON serialization of the value
%t      Converts to boolean and then produces "true" or "false"
%b      Converts to number, requires integer, produces binary representation
%d      Converts to number, requires integer, produces decimal representation
%o      Converts to number, requires integer, produces octal representation
%x      Converts to number, requires integer, produces hexadecimal representation
        with lowercase letters
%X      Like %x but with uppercase letters
%e      Converts to number, produces scientific notation like -1.234456e+78
%E      Like %e but with an uppercase "E" representing the exponent
%f      Converts to number, produces decimal representation with fractional
        part but no exponent, like 123.456
%g      %e for large exponents or %f otherwise
%G      %E for large exponents or %f otherwise
%s      Converts to string and produces the string's characters
%q      Converts to string and produces JSON-quoted string representation,
        like %v.

The default format selections made by %v are:

string  %s
number  %g
bool    %t
other   %#v

Null values produce the literal keyword "null" for %v and %#v, and produce an error otherwise.

Width is specified by an optional decimal number immediately preceding the verb letter. If absent, the width is whatever is necessary to represent the value. Precision is specified after the (optional) width by a period followed by a decimal number. If no period is present, a default precision is used. A period with no following number is invalid. For examples:

%f     default width, default precision
%9f    width 9, default precision
%.2f   default width, precision 2
%9.2f  width 9, precision 2

Width and precision are measured in unicode characters (grapheme clusters).

For most values, width is the minimum number of characters to output, padding the formatted form with spaces if necessary.

For strings, precision limits the length of the input to be formatted (not the size of the output), truncating if necessary.

For numbers, width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, except that for %g/%G precision sets the total number of significant digits.

The following additional symbols can be used immediately after the percent introducer as flags:

      (a space) leave a space where the sign would be if number is positive
+     Include a sign for a number even if it is positive (numeric only)
-     Pad with spaces on the left rather than the right
0     Pad with zeros rather than spaces.

Flag characters are ignored for verbs that do not support them.

By default, % sequences consume successive arguments starting with the first. Introducing a [n] sequence immediately before the verb letter, where n is a decimal integer, explicitly chooses a particular value argument by its one-based index. Subsequent calls without an explicit index will then proceed with n+1, n+2, etc.

An error is produced if the format string calls for an impossible conversion or accesses more values than are given. An error is produced also for an unsupported format verb.

func FormatDate

func FormatDate(format cty.Value, timestamp cty.Value) (cty.Value, error)

FormatDate reformats a timestamp given in RFC3339 syntax into another time syntax defined by a given format string.

The format string uses letter mnemonics to represent portions of the timestamp, with repetition signifying length variants of each portion. Single quote characters ' can be used to quote sequences of literal letters that should not be interpreted as formatting mnemonics.

The full set of supported mnemonic sequences is listed below:

YY       Year modulo 100 zero-padded to two digits, like "06".
YYYY     Four (or more) digit year, like "2006".
M        Month number, like "1" for January.
MM       Month number zero-padded to two digits, like "01".
MMM      English month name abbreviated to three letters, like "Jan".
MMMM     English month name unabbreviated, like "January".
D        Day of month number, like "2".
DD       Day of month number zero-padded to two digits, like "02".
EEE      English day of week name abbreviated to three letters, like "Mon".
EEEE     English day of week name unabbreviated, like "Monday".
h        24-hour number, like "2".
hh       24-hour number zero-padded to two digits, like "02".
H        12-hour number, like "2".
HH       12-hour number zero-padded to two digits, like "02".
AA       Hour AM/PM marker in uppercase, like "AM".
aa       Hour AM/PM marker in lowercase, like "am".
m        Minute within hour, like "5".
mm       Minute within hour zero-padded to two digits, like "05".
s        Second within minute, like "9".
ss       Second within minute zero-padded to two digits, like "09".
ZZZZ     Timezone offset with just sign and digit, like "-0800".
ZZZZZ    Timezone offset with colon separating hours and minutes, like "-08:00".
Z        Like ZZZZZ but with a special case "Z" for UTC.
ZZZ      Like ZZZZ but with a special case "UTC" for UTC.

The format syntax is optimized mainly for generating machine-oriented timestamps rather than human-oriented timestamps; the English language portions of the output reflect the use of English names in a number of machine-readable date formatting standards. For presentation to humans, a locale-aware time formatter (not included in this package) is a better choice.

The format syntax is not compatible with that of any other language, but is optimized so that patterns for common standard date formats can be recognized quickly even by a reader unfamiliar with the format syntax.

func FormatList

func FormatList(format cty.Value, vals ...cty.Value) (cty.Value, error)

FormatList applies the same formatting behavior as Format, but accepts a mixture of list and non-list values as arguments. Any list arguments passed must have the same length, which dictates the length of the resulting list.

Any non-list arguments are used repeatedly for each iteration over the list arguments. The list arguments are iterated in order by key, so corresponding items are formatted together.

func GreaterThan

func GreaterThan(a cty.Value, b cty.Value) (cty.Value, error)

GreaterThan returns true if a is less than b.

func GreaterThanOrEqualTo

func GreaterThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error)

GreaterThanOrEqualTo returns true if a is less than b.

func HasIndex

func HasIndex(collection cty.Value, key cty.Value) (cty.Value, error)

HasIndex determines whether the given collection can be indexed with the given key.

func Indent added in v1.3.0

func Indent(spaces, str cty.Value) (cty.Value, error)

Indent adds a given number of spaces to the beginnings of all but the first line in a given multi-line string.

func Index

func Index(collection cty.Value, key cty.Value) (cty.Value, error)

Index returns an element from the given collection using the given key, or returns an error if there is no element for the given key.

func Int

func Int(num cty.Value) (cty.Value, error)

Int removes the fractional component of the given number returning an integer representing the whole number component, rounding towards zero. For example, -1.5 becomes -1.

If an infinity is passed to Int, an error is returned.

func JSONDecode

func JSONDecode(str cty.Value) (cty.Value, error)

JSONDecode parses the given JSON string and, if it is valid, returns the value it represents.

Note that applying JSONDecode to the result of JSONEncode may not produce an identically-typed result, since JSON encoding is lossy for cty Types. The resulting value will consist only of primitive types, object types, and tuple types.

func JSONEncode

func JSONEncode(val cty.Value) (cty.Value, error)

JSONEncode returns a JSON serialization of the given value.

func Join added in v1.3.0

func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error)

Join concatenates together the string elements of one or more lists with a given separator.

func Keys added in v1.3.0

func Keys(inputMap cty.Value) (cty.Value, error)

Keys takes a map and returns a sorted list of the map keys.

func Length

func Length(collection cty.Value) (cty.Value, error)

Length returns the number of elements in the given collection.

func LessThan

func LessThan(a cty.Value, b cty.Value) (cty.Value, error)

LessThan returns true if a is less than b.

func LessThanOrEqualTo

func LessThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error)

LessThanOrEqualTo returns true if a is less than b.

func Log added in v1.3.0

func Log(num, base cty.Value) (cty.Value, error)

Log returns returns the logarithm of a given number in a given base.

func Lookup added in v1.3.0

func Lookup(inputMap, key, defaultValue cty.Value) (cty.Value, error)

Lookup performs a dynamic lookup into a map. There are three required arguments, inputMap and key, plus a defaultValue, which is a value to return if the given key is not found in the inputMap.

func Lower

func Lower(str cty.Value) (cty.Value, error)

Lower is a Function that converts a given string to lowercase.

func MakeToFunc added in v1.3.0

func MakeToFunc(wantTy cty.Type) function.Function

MakeToFunc constructs a "to..." function, like "tostring", which converts its argument to a specific type or type kind.

The given type wantTy can be any type constraint that cty's "convert" package would accept. In particular, this means that you can pass cty.List(cty.DynamicPseudoType) to mean "list of any single type", which will then cause cty to attempt to unify all of the element types when given a tuple.

func Max

func Max(numbers ...cty.Value) (cty.Value, error)

Max returns the maximum number from the given numbers.

func Merge added in v1.3.0

func Merge(maps ...cty.Value) (cty.Value, error)

Merge takes an arbitrary number of maps and returns a single map that contains a merged set of elements from all of the maps.

If more than one given map defines the same key then the one that is later in the argument sequence takes precedence.

func Min

func Min(numbers ...cty.Value) (cty.Value, error)

Min returns the minimum number from the given numbers.

func Modulo

func Modulo(a cty.Value, b cty.Value) (cty.Value, error)

Modulo returns the remainder of a divided by b under integer division, where both a and b are numbers.

func Multiply

func Multiply(a cty.Value, b cty.Value) (cty.Value, error)

Multiply returns the product of the two given numbers.

func Negate

func Negate(num cty.Value) (cty.Value, error)

Negate returns the given number multipled by -1.

func Not

func Not(num cty.Value) (cty.Value, error)

Not returns the logical complement of the given boolean value.

func NotEqual

func NotEqual(a cty.Value, b cty.Value) (cty.Value, error)

NotEqual is the opposite of Equal.

func Or

func Or(a, b cty.Value) (cty.Value, error)

Or returns true if either of the given boolean values are true.

func ParseInt added in v1.3.0

func ParseInt(num cty.Value, base cty.Value) (cty.Value, error)

ParseInt parses a string argument and returns an integer of the specified base.

func Pow added in v1.3.0

func Pow(num, power cty.Value) (cty.Value, error)

Pow returns the logarithm of a given number in a given base.

func Range

func Range(params ...cty.Value) (cty.Value, error)

Range creates a list of numbers by starting from the given starting value, then adding the given step value until the result is greater than or equal to the given stopping value. Each intermediate result becomes an element in the resulting list.

When all three parameters are set, the order is (start, end, step). If only two parameters are set, they are the start and end respectively and step defaults to 1. If only one argument is set, it gives the end value with start defaulting to 0 and step defaulting to 1.

Because the resulting list must be fully buffered in memory, there is an artificial cap of 1024 elements, after which this function will return an error to avoid consuming unbounded amounts of memory. The Range function is primarily intended for creating small lists of indices to iterate over, so there should be no reason to generate huge lists with it.

func Regex

func Regex(pattern, str cty.Value) (cty.Value, error)

Regex is a function that extracts one or more substrings from a given string by applying a regular expression pattern, describing the first match.

The return type depends on the composition of the capture groups (if any) in the pattern:

  • If there are no capture groups at all, the result is a single string representing the entire matched pattern.
  • If all of the capture groups are named, the result is an object whose keys are the named groups and whose values are their sub-matches, or null if a particular sub-group was inside another group that didn't match.
  • If none of the capture groups are named, the result is a tuple whose elements are the sub-groups in order and whose values are their sub-matches, or null if a particular sub-group was inside another group that didn't match.
  • It is invalid to use both named and un-named capture groups together in the same pattern.

If the pattern doesn't match, this function returns an error. To test for a match, call RegexAll and check if the length of the result is greater than zero.

func RegexAll

func RegexAll(pattern, str cty.Value) (cty.Value, error)

RegexAll is similar to Regex but it finds all of the non-overlapping matches in the given string and returns a list of them.

The result type is always a list, whose element type is deduced from the pattern in the same way as the return type for Regex is decided.

If the pattern doesn't match at all, this function returns an empty list.

func RegexReplace added in v1.4.0

func RegexReplace(str, substr, replace cty.Value) (cty.Value, error)

func Replace added in v1.4.0

func Replace(str, substr, replace cty.Value) (cty.Value, error)

Replace searches a given string for another given substring, and replaces all occurrences with a given replacement string.

func Reverse

func Reverse(str cty.Value) (cty.Value, error)

Reverse is a Function that reverses the order of the characters in the given string.

As usual, "character" for the sake of this function is a grapheme cluster, so combining diacritics (for example) will be considered together as a single character.

func ReverseList added in v1.3.0

func ReverseList(list cty.Value) (cty.Value, error)

ReverseList takes a sequence and produces a new sequence of the same length with all of the same elements as the given sequence but in reverse order.

func SetHasElement

func SetHasElement(set cty.Value, elem cty.Value) (cty.Value, error)

SetHasElement determines whether the given set contains the given value as an element.

func SetIntersection

func SetIntersection(sets ...cty.Value) (cty.Value, error)

Intersection returns a new set containing the elements that exist in all of the given sets, which must have element types that can all be converted to some common type using the standard type unification rules. If conversion is not possible, an error is returned.

The intersection operation is performed after type conversion, which may result in some previously-distinct values being conflated.

At least one set must be provided.

func SetProduct added in v1.3.0

func SetProduct(sets ...cty.Value) (cty.Value, error)

SetProduct computes the Cartesian product of sets or sequences.

func SetSubtract

func SetSubtract(a, b cty.Value) (cty.Value, error)

SetSubtract returns a new set containing the elements from the first set that are not present in the second set. The sets must have element types that can both be converted to some common type using the standard type unification rules. If conversion is not possible, an error is returned.

The subtract operation is performed after type conversion, which may result in some previously-distinct values being conflated.

func SetSymmetricDifference

func SetSymmetricDifference(sets ...cty.Value) (cty.Value, error)

SetSymmetricDifference returns a new set containing elements that appear in any of the given sets but not multiple. The sets must have element types that can all be converted to some common type using the standard type unification rules. If conversion is not possible, an error is returned.

The difference operation is performed after type conversion, which may result in some previously-distinct values being conflated.

func SetUnion

func SetUnion(sets ...cty.Value) (cty.Value, error)

SetUnion returns a new set containing all of the elements from the given sets, which must have element types that can all be converted to some common type using the standard type unification rules. If conversion is not possible, an error is returned.

The union operation is performed after type conversion, which may result in some previously-distinct values being conflated.

At least one set must be provided.

func Signum added in v1.3.0

func Signum(num cty.Value) (cty.Value, error)

Signum determines the sign of a number, returning a number between -1 and 1 to represent the sign.

func Slice added in v1.3.0

func Slice(list, start, end cty.Value) (cty.Value, error)

Slice extracts some consecutive elements from within a list.

func Sort added in v1.3.0

func Sort(list cty.Value) (cty.Value, error)

Sort re-orders the elements of a given list of strings so that they are in ascending lexicographical order.

func Split added in v1.3.0

func Split(sep, str cty.Value) (cty.Value, error)

Split divides a given string by a given separator, returning a list of strings containing the characters between the separator sequences.

func Strlen

func Strlen(str cty.Value) (cty.Value, error)

Strlen is a Function that returns the length of the given string in characters.

As usual, "character" for the sake of this function is a grapheme cluster, so combining diacritics (for example) will be considered together as a single character.

func Substr

func Substr(str cty.Value, offset cty.Value, length cty.Value) (cty.Value, error)

Substr is a Function that extracts a sequence of characters from another string and creates a new string.

As usual, "character" for the sake of this function is a grapheme cluster, so combining diacritics (for example) will be considered together as a single character.

The "offset" index may be negative, in which case it is relative to the end of the given string.

The "length" may be -1, in which case the remainder of the string after the given offset will be returned.

func Subtract

func Subtract(a cty.Value, b cty.Value) (cty.Value, error)

Subtract returns the difference between the two given numbers.

func TimeAdd added in v1.3.0

func TimeAdd(timestamp cty.Value, duration cty.Value) (cty.Value, error)

TimeAdd adds a duration to a timestamp, returning a new timestamp.

In the HCL language, timestamps are conventionally represented as strings using RFC 3339 "Date and Time format" syntax. Timeadd requires the timestamp argument to be a string conforming to this syntax.

`duration` is a string representation of a time difference, consisting of sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted units are `ns`, `us` (or `µs`), `"ms"`, `"s"`, `"m"`, and `"h"`. The first number may be negative to indicate a negative duration, like `"-2h5m"`.

The result is a string, also in RFC 3339 format, representing the result of adding the given direction to the given timestamp.

func Title added in v1.3.0

func Title(str cty.Value) (cty.Value, error)

Title converts the first letter of each word in the given string to uppercase.

func Trim added in v1.3.0

func Trim(str, cutset cty.Value) (cty.Value, error)

Trim removes the specified characters from the start and end of the given string.

func TrimPrefix added in v1.3.0

func TrimPrefix(str, prefix cty.Value) (cty.Value, error)

TrimPrefix removes the specified prefix from the start of the given string.

func TrimSpace added in v1.3.0

func TrimSpace(str cty.Value) (cty.Value, error)

TrimSpace removes any space characters from the start and end of the given string.

func TrimSuffix added in v1.3.0

func TrimSuffix(str, suffix cty.Value) (cty.Value, error)

TrimSuffix removes the specified suffix from the end of the given string.

func Upper

func Upper(str cty.Value) (cty.Value, error)

Upper is a Function that converts a given string to uppercase.

func Values added in v1.3.0

func Values(values cty.Value) (cty.Value, error)

Values returns a list of the map values, in the order of the sorted keys. This function only works on flat maps.

func Zipmap added in v1.3.0

func Zipmap(keys, values cty.Value) (cty.Value, error)

Zipmap constructs a map from a list of keys and a corresponding list of values.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL