Bump github.com/hashicorp/terraform-plugin-sdk/v2 from 2.24.1 to 2.26.0

Bumps [github.com/hashicorp/terraform-plugin-sdk/v2](https://github.com/hashicorp/terraform-plugin-sdk) from 2.24.1 to 2.26.0.
- [Release notes](https://github.com/hashicorp/terraform-plugin-sdk/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-sdk/compare/v2.24.1...v2.26.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-sdk/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2023-03-20 20:25:53 +00:00
committed by GitHub
parent 4f33464489
commit 84c9110a24
502 changed files with 39722 additions and 9679 deletions

View File

@ -15,7 +15,8 @@ var NotFunc = function.New(&function.Spec{
AllowMarked: true,
},
},
Type: function.StaticReturnType(cty.Bool),
Type: function.StaticReturnType(cty.Bool),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return args[0].Not(), nil
},
@ -37,7 +38,8 @@ var AndFunc = function.New(&function.Spec{
AllowMarked: true,
},
},
Type: function.StaticReturnType(cty.Bool),
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
},
@ -59,7 +61,8 @@ var OrFunc = function.New(&function.Spec{
AllowMarked: true,
},
},
Type: function.StaticReturnType(cty.Bool),
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 File

@ -38,7 +38,8 @@ var BytesLenFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
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
@ -65,7 +66,8 @@ var BytesSliceFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(Bytes),
Type: function.StaticReturnType(Bytes),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
bufPtr := args[0].EncapsulatedValue().(*[]byte)

View File

@ -32,6 +32,7 @@ var HasIndexFunc = function.New(&function.Spec{
}
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
},
@ -114,6 +115,7 @@ var LengthFunc = function.New(&function.Spec{
Name: "collection",
Type: cty.DynamicPseudoType,
AllowDynamicType: true,
AllowUnknown: true,
AllowMarked: true,
},
},
@ -124,6 +126,7 @@ var LengthFunc = function.New(&function.Spec{
}
return cty.Number, nil
},
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
return args[0].Length(), nil
},
@ -251,6 +254,7 @@ var CoalesceListFunc = function.New(&function.Spec{
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() {
@ -283,7 +287,8 @@ var CompactFunc = function.New(&function.Spec{
Type: cty.List(cty.String),
},
},
Type: function.StaticReturnType(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() {
@ -324,7 +329,8 @@ var ContainsFunc = function.New(&function.Spec{
Type: cty.DynamicPseudoType,
},
},
Type: function.StaticReturnType(cty.Bool),
Type: function.StaticReturnType(cty.Bool),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
arg := args[0]
ty := arg.Type()
@ -382,6 +388,7 @@ var DistinctFunc = function.New(&function.Spec{
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]
@ -426,6 +433,7 @@ var ChunklistFunc = function.New(&function.Spec{
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]
@ -513,6 +521,7 @@ var FlattenFunc = function.New(&function.Spec{
}
return cty.Tuple(tys), nil
},
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
inputList := args[0]
@ -611,6 +620,7 @@ var KeysFunc = function.New(&function.Spec{
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) {
// We must unmark the value before we can use ElementIterator on it, and
// then re-apply the same marks (possibly none) when we return. Since we
@ -832,6 +842,7 @@ var MergeFunc = function.New(&function.Spec{
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
@ -891,6 +902,7 @@ var ReverseListFunc = function.New(&function.Spec{
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()
@ -919,10 +931,11 @@ 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,
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 {
@ -964,6 +977,7 @@ var SetProductFunc = function.New(&function.Spec{
}
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
@ -976,7 +990,7 @@ var SetProductFunc = function.New(&function.Spec{
// Continue processing after we find an argument with unknown
// length to ensure that we cover all the marks
if !arg.Length().IsKnown() {
if !(arg.IsKnown() && arg.Length().IsKnown()) {
hasUnknownLength = true
continue
}
@ -988,7 +1002,62 @@ var SetProductFunc = function.New(&function.Spec{
}
if hasUnknownLength {
return cty.UnknownVal(retType).WithMarks(retMarks), nil
defer func() {
// We're definitely going to return from somewhere in this
// branch and however we do it we must reapply the marks
// on the way out.
ret = ret.WithMarks(retMarks)
}()
ret := cty.UnknownVal(retType)
// Even if we don't know the exact length we may be able to
// constrain the upper and lower bounds of the resulting length.
maxLength := 1
for _, arg := range args {
arg, _ := arg.Unmark() // safe to discard marks because "retMarks" already contains them all
argRng := arg.Range()
ty := argRng.TypeConstraint()
var argMaxLen int
if ty.IsCollectionType() {
argMaxLen = argRng.LengthUpperBound()
} else if ty.IsTupleType() {
argMaxLen = ty.Length()
} else {
// Should not get here but if we do then we'll just
// bail out with an unrefined unknown value.
return ret, nil
}
// The upper bound of a totally-unrefined collection is
// math.MaxInt, which will quickly get us to integer overflow
// here, and so out of pragmatism we'll just impose a reasonable
// upper limit on what is a useful bound to track and return
// unrefined for unusually-large input.
if argMaxLen > 1024 { // arbitrarily-decided threshold
return ret, nil
}
maxLength *= argMaxLen
if maxLength > 2048 { // arbitrarily-decided threshold
return ret, nil
}
if maxLength < 0 { // Seems like we already overflowed, then.
return ret, nil
}
}
if maxLength == 0 {
// This refinement will typically allow the unknown value to
// collapse into a known empty collection.
ret = ret.Refine().CollectionLength(0).NewValue()
} else {
// If we know there's a nonzero maximum number of elements then
// set element coalescing cannot reduce to fewer than one
// element.
ret = ret.Refine().
CollectionLengthLowerBound(1).
CollectionLengthUpperBound(maxLength).
NewValue()
}
return ret, nil
}
if total == 0 {
@ -1101,6 +1170,7 @@ var SliceFunc = function.New(&function.Spec{
}
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()
@ -1215,6 +1285,7 @@ var ValuesFunc = function.New(&function.Spec{
}
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]
@ -1303,6 +1374,7 @@ var ZipmapFunc = function.New(&function.Spec{
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]

View File

@ -87,3 +87,36 @@ func MakeToFunc(wantTy cty.Type) function.Function {
},
})
}
// 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.
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,
// NOTE: We intentionally don't set AllowNull here, and so
// the function system will automatically reject a null argument
// for us before calling Impl.
},
},
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) {
// Our argument doesn't set AllowNull: true, so we're guaranteed to
// have a non-null value in args[0].
return args[0], nil
},
})
func AssertNotNull(v cty.Value) (cty.Value, error) {
return AssertNotNullFunc.Call([]cty.Value{v})
}

View File

@ -43,6 +43,7 @@ var CSVDecodeFunc = function.New(&function.Spec{
}
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()

View File

@ -23,7 +23,8 @@ var FormatDateFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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()
@ -281,67 +282,6 @@ func FormatDate(format cty.Value, timestamp cty.Value) (cty.Value, error) {
return FormatDateFunc.Call([]cty.Value{format, timestamp})
}
func parseTimestamp(ts string) (time.Time, error) {
t, err := time.Parse(time.RFC3339, ts)
if err != nil {
switch err := err.(type) {
case *time.ParseError:
// If err is s time.ParseError then its string representation is not
// appropriate since it relies on details of Go's strange date format
// representation, which a caller of our functions is not expected
// to be familiar with.
//
// Therefore we do some light transformation to get a more suitable
// error that should make more sense to our callers. These are
// still not awesome error messages, but at least they refer to
// the timestamp portions by name rather than by Go's example
// values.
if err.LayoutElem == "" && err.ValueElem == "" && err.Message != "" {
// For some reason err.Message is populated with a ": " prefix
// by the time package.
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp%s", err.Message)
}
var what string
switch err.LayoutElem {
case "2006":
what = "year"
case "01":
what = "month"
case "02":
what = "day of month"
case "15":
what = "hour"
case "04":
what = "minute"
case "05":
what = "second"
case "Z07:00":
what = "UTC offset"
case "T":
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: missing required time introducer 'T'")
case ":", "-":
if err.ValueElem == "" {
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string where %q is expected", err.LayoutElem)
} else {
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: found %q where %q is expected", err.ValueElem, err.LayoutElem)
}
default:
// Should never get here, because time.RFC3339 includes only the
// above portions, but since that might change in future we'll
// be robust here.
what = "timestamp segment"
}
if err.ValueElem == "" {
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string before %s", what)
} else {
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: cannot use %q as %s", err.ValueElem, what)
}
}
return time.Time{}, err
}
return t, nil
}
// splitDataFormat is a bufio.SplitFunc used to tokenize a date format.
func splitDateFormat(data []byte, atEOF bool) (advance int, token []byte, err error) {
if len(data) == 0 {
@ -418,6 +358,75 @@ func startsDateFormatVerb(b byte) bool {
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
}
func parseTimestamp(ts string) (time.Time, error) {
t, err := parseStrictRFC3339(ts)
if err != nil {
switch err := err.(type) {
case *time.ParseError:
// If err is s time.ParseError then its string representation is not
// appropriate since it relies on details of Go's strange date format
// representation, which a caller of our functions is not expected
// to be familiar with.
//
// Therefore we do some light transformation to get a more suitable
// error that should make more sense to our callers. These are
// still not awesome error messages, but at least they refer to
// the timestamp portions by name rather than by Go's example
// values.
if err.LayoutElem == "" && err.ValueElem == "" && err.Message != "" {
// For some reason err.Message is populated with a ": " prefix
// by the time package.
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp%s", err.Message)
}
var what string
switch err.LayoutElem {
case "2006":
what = "year"
case "01":
what = "month"
case "02":
what = "day of month"
case "15":
what = "hour"
case "04":
what = "minute"
case "05":
what = "second"
case "Z07:00":
what = "UTC offset"
case "T":
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: missing required time introducer 'T'")
case ":", "-":
if err.ValueElem == "" {
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string where %q is expected", err.LayoutElem)
} else {
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: found %q where %q is expected", err.ValueElem, err.LayoutElem)
}
default:
// Should never get here, because RFC3339 includes only the
// above portions.
what = "timestamp segment"
}
if err.ValueElem == "" {
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string before %s", what)
} else {
switch {
case what == "hour" && strings.Contains(err.ValueElem, ":"):
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: hour must be between 0 and 23 inclusive")
case what == "hour" && len(err.ValueElem) != 2:
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: hour must have exactly two digits")
case what == "minute" && len(err.ValueElem) != 2:
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: minute must have exactly two digits")
default:
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: cannot use %q as %s", err.ValueElem, what)
}
}
}
return time.Time{}, err
}
return t, nil
}
// TimeAdd adds a duration to a timestamp, returning a new timestamp.
//
// In the HCL language, timestamps are conventionally represented as

View File

@ -0,0 +1,219 @@
package stdlib
import (
"errors"
"strconv"
"time"
)
// This file inlines some RFC3339 parsing code that was added to the Go standard
// library's "time" package during the Go 1.20 development period but then
// reverted prior to release to follow the Go proposals process first.
//
// Our goal is to support only valid RFC3339 strings regardless of what version
// of Go is being used, because the Go stdlib is just an implementation detail
// of the cty stdlib and so these functions should not very their behavior
// significantly due to being compiled against a different Go version.
//
// These inline copies of the code from upstream should likely stay here
// indefinitely even if functionality like this _is_ accepted in a later version
// of Go, because this now defines cty's definition of RFC3339 parsing as
// intentionally independent of Go's.
func parseStrictRFC3339(str string) (time.Time, error) {
t, ok := parseRFC3339(str)
if !ok {
// If parsing failed then we'll try to use time.Parse to gather up a
// helpful error object.
_, err := time.Parse(time.RFC3339, str)
if err != nil {
return time.Time{}, err
}
// The parse template syntax cannot correctly validate RFC 3339.
// Explicitly check for cases that Parse is unable to validate for.
// See https://go.dev/issue/54580.
num2 := func(str string) byte { return 10*(str[0]-'0') + (str[1] - '0') }
switch {
case str[len("2006-01-02T")+1] == ':': // hour must be two digits
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: "15",
ValueElem: str[len("2006-01-02T"):][:1],
Message: ": hour must have two digits",
}
case str[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: ".",
ValueElem: ",",
Message: ": sub-second separator must be a period",
}
case str[len(str)-1] != 'Z':
switch {
case num2(str[len(str)-len("07:00"):]) >= 24: // timezone hour must be in range
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: "Z07:00",
ValueElem: str[len(str)-len("Z07:00"):],
Message: ": timezone hour out of range",
}
case num2(str[len(str)-len("00"):]) >= 60: // timezone minute must be in range
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: "Z07:00",
ValueElem: str[len(str)-len("Z07:00"):],
Message: ": timezone minute out of range",
}
}
default: // unknown error; should not occur
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: time.RFC3339,
ValueElem: str,
Message: "",
}
}
}
return t, nil
}
func parseRFC3339(s string) (time.Time, bool) {
// parseUint parses s as an unsigned decimal integer and
// verifies that it is within some range.
// If it is invalid or out-of-range,
// it sets ok to false and returns the min value.
ok := true
parseUint := func(s string, min, max int) (x int) {
for _, c := range []byte(s) {
if c < '0' || '9' < c {
ok = false
return min
}
x = x*10 + int(c) - '0'
}
if x < min || max < x {
ok = false
return min
}
return x
}
// Parse the date and time.
if len(s) < len("2006-01-02T15:04:05") {
return time.Time{}, false
}
year := parseUint(s[0:4], 0, 9999) // e.g., 2006
month := parseUint(s[5:7], 1, 12) // e.g., 01
day := parseUint(s[8:10], 1, daysIn(time.Month(month), year)) // e.g., 02
hour := parseUint(s[11:13], 0, 23) // e.g., 15
min := parseUint(s[14:16], 0, 59) // e.g., 04
sec := parseUint(s[17:19], 0, 59) // e.g., 05
if !ok || !(s[4] == '-' && s[7] == '-' && s[10] == 'T' && s[13] == ':' && s[16] == ':') {
return time.Time{}, false
}
s = s[19:]
// Parse the fractional second.
var nsec int
if len(s) >= 2 && s[0] == '.' && isDigit(s, 1) {
n := 2
for ; n < len(s) && isDigit(s, n); n++ {
}
nsec, _, _ = parseNanoseconds(s, n)
s = s[n:]
}
// Parse the time zone.
loc := time.UTC
if len(s) != 1 || s[0] != 'Z' {
if len(s) != len("-07:00") {
return time.Time{}, false
}
hr := parseUint(s[1:3], 0, 23) // e.g., 07
mm := parseUint(s[4:6], 0, 59) // e.g., 00
if !ok || !((s[0] == '-' || s[0] == '+') && s[3] == ':') {
return time.Time{}, false
}
zoneOffsetSecs := (hr*60 + mm) * 60
if s[0] == '-' {
zoneOffsetSecs = -zoneOffsetSecs
}
loc = time.FixedZone("", zoneOffsetSecs)
}
t := time.Date(year, time.Month(month), day, hour, min, sec, nsec, loc)
return t, true
}
func isDigit(s string, i int) bool {
if len(s) <= i {
return false
}
c := s[i]
return '0' <= c && c <= '9'
}
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
if value[0] != '.' && value[0] != ',' {
err = errBadTimestamp
return
}
if nbytes > 10 {
value = value[:10]
nbytes = 10
}
if ns, err = strconv.Atoi(value[1:nbytes]); err != nil {
return
}
if ns < 0 {
rangeErrString = "fractional second"
return
}
// We need nanoseconds, which means scaling by the number
// of missing digits in the format, maximum length 10.
scaleDigits := 10 - nbytes
for i := 0; i < scaleDigits; i++ {
ns *= 10
}
return
}
// These are internal errors used by the date parsing code and are not ever
// returned by public functions.
var errBadTimestamp = errors.New("bad value for field")
// daysBefore[m] counts the number of days in a non-leap year
// before month m begins. There is an entry for m=12, counting
// the number of days before January of next year (365).
var daysBefore = [...]int32{
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}
func daysIn(m time.Month, year int) int {
if m == time.February && isLeap(year) {
return 29
}
return int(daysBefore[m] - daysBefore[m-1])
}
func isLeap(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}

View File

@ -26,17 +26,27 @@ var FormatFunc = function.New(&function.Spec{
},
},
VarParam: &function.Parameter{
Name: "args",
Type: cty.DynamicPseudoType,
AllowNull: true,
Name: "args",
Type: cty.DynamicPseudoType,
AllowNull: true,
AllowUnknown: true,
},
Type: function.StaticReturnType(cty.String),
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() {
// We require all nested values to be known because the only
// thing we can do for a collection/structural type is print
// it as JSON and that requires it to be wholly known.
// However, we might be able to refine the result with a
// known prefix, if there are literal characters before the
// first formatting verb.
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
}
}
@ -59,7 +69,8 @@ var FormatListFunc = function.New(&function.Spec{
AllowNull: true,
AllowUnknown: true,
},
Type: function.StaticReturnType(cty.List(cty.String)),
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:]
@ -164,7 +175,7 @@ var FormatListFunc = function.New(&function.Spec{
// We require all nested values to be known because the only
// thing we can do for a collection/structural type is print
// it as JSON and that requires it to be wholly known.
ret = append(ret, cty.UnknownVal(cty.String))
ret = append(ret, cty.UnknownVal(cty.String).RefineNotNull())
continue Results
}
}

View File

@ -26,7 +26,8 @@ var EqualFunc = function.New(&function.Spec{
AllowNull: true,
},
},
Type: function.StaticReturnType(cty.Bool),
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
},
@ -50,7 +51,8 @@ var NotEqualFunc = function.New(&function.Spec{
AllowNull: true,
},
},
Type: function.StaticReturnType(cty.Bool),
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
},
@ -77,6 +79,7 @@ var CoalesceFunc = function.New(&function.Spec{
}
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() {
@ -92,6 +95,10 @@ var CoalesceFunc = function.New(&function.Spec{
},
})
func refineNonNull(b *cty.RefinementBuilder) *cty.RefinementBuilder {
return b.NotNull()
}
// Equal determines whether the two given values are equal, returning a
// bool value.
func Equal(a cty.Value, b cty.Value) (cty.Value, error) {

View File

@ -1,6 +1,10 @@
package stdlib
import (
"bytes"
"strings"
"unicode/utf8"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/json"
@ -12,18 +16,40 @@ var JSONEncodeFunc = function.New(&function.Spec{
{
Name: "val",
Type: cty.DynamicPseudoType,
AllowUnknown: true,
AllowDynamicType: true,
AllowNull: true,
},
},
Type: function.StaticReturnType(cty.String),
Type: function.StaticReturnType(cty.String),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
val := args[0]
if !val.IsWhollyKnown() {
// We can't serialize unknowns, so if the value is unknown or
// contains any _nested_ unknowns then our result must be
// unknown.
return cty.UnknownVal(retType), nil
// unknown. However, we might still be able to at least constrain
// the prefix of our string so that downstreams can sniff for
// whether it's valid JSON and what result types it could have.
valRng := val.Range()
if valRng.CouldBeNull() {
// If null is possible then we can't constrain the result
// beyond the type constraint, because the very first character
// of the string is what distinguishes a null.
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() {
@ -35,6 +61,11 @@ var JSONEncodeFunc = function.New(&function.Spec{
return cty.NilVal, err
}
// json.Marshal should already produce a trimmed string, but we'll
// make sure it always is because our unknown value refinements above
// assume there will be no leading whitespace before the value.
buf = bytes.TrimSpace(buf)
return cty.StringVal(string(buf)), nil
},
})
@ -50,6 +81,42 @@ var JSONDecodeFunc = function.New(&function.Spec{
Type: func(args []cty.Value) (cty.Type, error) {
str := args[0]
if !str.IsKnown() {
// If the string isn't known then we can't fully parse it, but
// if the value has been refined with a prefix then we may at
// least be able to reject obviously-invalid syntax and maybe
// even predict the result type. It's safe to return a specific
// result type only if parsing a full document with this prefix
// would return exactly that type or fail with a syntax error.
rng := str.Range()
if prefix := strings.TrimSpace(rng.StringPrefix()); prefix != "" {
// If we know at least one character then it should be one
// of the few characters that can introduce a JSON value.
switch r, _ := utf8.DecodeRuneInString(prefix); r {
case '{', '[':
// These can start object values and array values
// respectively, but we can't actually form a full
// object type constraint or tuple type constraint
// without knowing all of the attributes, so we
// will still return DynamicPseudoType in this case.
case '"':
// This means that the result will either be a string
// or parsing will fail.
return cty.String, nil
case 't', 'f':
// Must either be a boolean value or a syntax error.
return cty.Bool, nil
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
// These characters would all start the "number" production.
return cty.Number, nil
case 'n':
// n is valid to begin the keyword "null" but that doesn't
// give us any extra type information.
default:
// No other characters are valid as the beginning of a
// JSON value, so we can safely return an early error.
return cty.NilType, function.NewArgErrorf(0, "a JSON document cannot begin with the character %q", r)
}
}
return cty.DynamicPseudoType, nil
}

View File

@ -20,7 +20,8 @@ var AbsoluteFunc = function.New(&function.Spec{
AllowMarked: true,
},
},
Type: function.StaticReturnType(cty.Number),
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return args[0].Absolute(), nil
},
@ -40,7 +41,8 @@ var AddFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
// big.Float.Add can panic if the input values are opposing infinities,
// so we must catch that here in order to remain within
@ -74,7 +76,8 @@ var SubtractFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
// big.Float.Sub can panic if the input values are infinities,
// so we must catch that here in order to remain within
@ -108,7 +111,8 @@ var MultiplyFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
// big.Float.Mul can panic if the input values are both zero or both
// infinity, so we must catch that here in order to remain within
@ -143,7 +147,8 @@ var DivideFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
// big.Float.Quo can panic if the input values are both zero or both
// infinity, so we must catch that here in order to remain within
@ -178,7 +183,8 @@ var ModuloFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
// big.Float.Mul can panic if the input values are both zero or both
// infinity, so we must catch that here in order to remain within
@ -205,17 +211,20 @@ var GreaterThanFunc = function.New(&function.Spec{
{
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),
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
},
@ -227,17 +236,20 @@ var GreaterThanOrEqualToFunc = function.New(&function.Spec{
{
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),
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
},
@ -249,17 +261,20 @@ var LessThanFunc = function.New(&function.Spec{
{
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),
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
},
@ -271,17 +286,20 @@ var LessThanOrEqualToFunc = function.New(&function.Spec{
{
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),
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
},
@ -297,7 +315,8 @@ var NegateFunc = function.New(&function.Spec{
AllowMarked: true,
},
},
Type: function.StaticReturnType(cty.Number),
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return args[0].Negate(), nil
},
@ -311,7 +330,8 @@ var MinFunc = function.New(&function.Spec{
Type: cty.Number,
AllowDynamicType: true,
},
Type: function.StaticReturnType(cty.Number),
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")
@ -336,7 +356,8 @@ var MaxFunc = function.New(&function.Spec{
Type: cty.Number,
AllowDynamicType: true,
},
Type: function.StaticReturnType(cty.Number),
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")
@ -362,7 +383,8 @@ var IntFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
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() {
@ -384,7 +406,8 @@ var CeilFunc = function.New(&function.Spec{
Type: cty.Number,
},
},
Type: function.StaticReturnType(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()
@ -414,7 +437,8 @@ var FloorFunc = function.New(&function.Spec{
Type: cty.Number,
},
},
Type: function.StaticReturnType(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()
@ -447,7 +471,8 @@ var LogFunc = function.New(&function.Spec{
Type: cty.Number,
},
},
Type: function.StaticReturnType(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 {
@ -476,7 +501,8 @@ var PowFunc = function.New(&function.Spec{
Type: cty.Number,
},
},
Type: function.StaticReturnType(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 {
@ -502,7 +528,8 @@ var SignumFunc = function.New(&function.Spec{
Type: cty.Number,
},
},
Type: function.StaticReturnType(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 {
@ -539,6 +566,7 @@ var ParseIntFunc = function.New(&function.Spec{
}
return cty.Number, nil
},
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
var numstr string

View File

@ -33,6 +33,7 @@ var RegexFunc = function.New(&function.Spec{
}
return retTy, err
},
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
if retType == cty.DynamicPseudoType {
return cty.DynamicVal, nil
@ -79,6 +80,7 @@ var RegexAllFunc = function.New(&function.Spec{
}
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 {

View File

@ -74,6 +74,7 @@ var ConcatFunc = function.New(&function.Spec{
}
return cty.Tuple(etys), nil
},
RefineResult: refineNonNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
switch {
case retType.IsListType():
@ -143,7 +144,8 @@ var RangeFunc = function.New(&function.Spec{
Name: "params",
Type: cty.Number,
},
Type: function.StaticReturnType(cty.List(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) {

View File

@ -23,7 +23,8 @@ var SetHasElementFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Bool),
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
},
@ -43,7 +44,8 @@ var SetUnionFunc = function.New(&function.Spec{
Type: cty.Set(cty.DynamicPseudoType),
AllowDynamicType: true,
},
Type: setOperationReturnType,
Type: setOperationReturnType,
RefineResult: refineNonNull,
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
return s1.Union(s2)
}, true),
@ -63,7 +65,8 @@ var SetIntersectionFunc = function.New(&function.Spec{
Type: cty.Set(cty.DynamicPseudoType),
AllowDynamicType: true,
},
Type: setOperationReturnType,
Type: setOperationReturnType,
RefineResult: refineNonNull,
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
return s1.Intersection(s2)
}, false),
@ -83,7 +86,8 @@ var SetSubtractFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: setOperationReturnType,
Type: setOperationReturnType,
RefineResult: refineNonNull,
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
return s1.Subtract(s2)
}, false),
@ -103,7 +107,8 @@ var SetSymmetricDifferenceFunc = function.New(&function.Spec{
Type: cty.Set(cty.DynamicPseudoType),
AllowDynamicType: true,
},
Type: setOperationReturnType,
Type: setOperationReturnType,
RefineResult: refineNonNull,
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
return s1.SymmetricDifference(s2)
}, false),

View File

@ -22,7 +22,8 @@ var UpperFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.String),
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)
@ -39,7 +40,8 @@ var LowerFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.String),
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)
@ -56,7 +58,8 @@ var ReverseFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.String),
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))
@ -81,25 +84,46 @@ var StrlenFunc = function.New(&function.Spec{
{
Name: "str",
Type: cty.String,
AllowUnknown: true,
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
RefineResult: func(b *cty.RefinementBuilder) *cty.RefinementBuilder {
// String length is never null and never negative.
// (We might refine the lower bound even more inside Impl.)
return b.NotNull().NumberRangeLowerBound(cty.NumberIntVal(0), true)
},
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
in := args[0].AsString()
l := 0
inB := []byte(in)
for i := 0; i < len(in); {
d, _, _ := textseg.ScanGraphemeClusters(inB[i:], true)
l++
i += d
if !args[0].IsKnown() {
ret := cty.UnknownVal(cty.Number)
// We may be able to still return a constrained result based on the
// refined range of the unknown value.
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
},
})
func graphemeClusterCount(in string) int {
l := 0
inB := []byte(in)
for i := 0; i < len(in); {
d, _, _ := textseg.ScanGraphemeClusters(inB[i:], true)
l++
i += d
}
return l
}
var SubstrFunc = function.New(&function.Spec{
Description: "Extracts a substring from the given string.",
Params: []function.Parameter{
@ -122,7 +146,8 @@ var SubstrFunc = function.New(&function.Spec{
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.String),
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
@ -218,7 +243,8 @@ var JoinFunc = function.New(&function.Spec{
Description: "One or more lists of strings to join.",
Type: cty.List(cty.String),
},
Type: function.StaticReturnType(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:]
@ -258,18 +284,29 @@ 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),
Name: "list",
Type: cty.List(cty.String),
AllowUnknown: true,
},
},
Type: function.StaticReturnType(cty.List(cty.String)),
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() {
// If some of the element values aren't known yet then we
// can't yet predict the order of the result.
return cty.UnknownVal(retType), nil
// can't yet predict the order of the result, but we can be
// sure that the length won't change.
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 { // Easy path
return listVal, nil
@ -307,7 +344,8 @@ var SplitFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.List(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()
@ -333,7 +371,8 @@ var ChompFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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
@ -356,7 +395,8 @@ var IndentFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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 {
@ -378,7 +418,8 @@ var TitleFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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
},
@ -394,7 +435,8 @@ var TrimSpaceFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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
},
@ -416,7 +458,8 @@ var TrimFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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()
@ -443,7 +486,8 @@ var TrimPrefixFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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()
@ -467,7 +511,8 @@ var TrimSuffixFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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()

View File

@ -30,7 +30,8 @@ var ReplaceFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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()
@ -59,7 +60,8 @@ var RegexReplaceFunc = function.New(&function.Spec{
Type: cty.String,
},
},
Type: function.StaticReturnType(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()