add vendor
This commit is contained in:
78
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bool.go
generated
vendored
Normal file
78
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bool.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var NotFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "val",
|
||||
Type: cty.Bool,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Not(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var AndFunc = function.New(&function.Spec{
|
||||
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),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].And(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var OrFunc = function.New(&function.Spec{
|
||||
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),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Or(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Not returns the logical complement of the given boolean value.
|
||||
func Not(num cty.Value) (cty.Value, error) {
|
||||
return NotFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// And returns true if and only if both of the given boolean values are true.
|
||||
func And(a, b cty.Value) (cty.Value, error) {
|
||||
return AndFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Or returns true if either of the given boolean values are true.
|
||||
func Or(a, b cty.Value) (cty.Value, error) {
|
||||
return OrFunc.Call([]cty.Value{a, b})
|
||||
}
|
112
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bytes.go
generated
vendored
Normal file
112
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bytes.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
// 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.
|
||||
var Bytes = cty.Capsule("bytes", reflect.TypeOf([]byte(nil)))
|
||||
|
||||
// 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 BytesVal(buf []byte) cty.Value {
|
||||
if buf == nil {
|
||||
panic("can't make Bytes value from nil slice")
|
||||
}
|
||||
|
||||
return cty.CapsuleVal(Bytes, &buf)
|
||||
}
|
||||
|
||||
// BytesLen is a Function that returns the length of the buffer encapsulated
|
||||
// in a Bytes value.
|
||||
var BytesLenFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "buf",
|
||||
Type: Bytes,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
bufPtr := args[0].EncapsulatedValue().(*[]byte)
|
||||
return cty.NumberIntVal(int64(len(*bufPtr))), nil
|
||||
},
|
||||
})
|
||||
|
||||
// BytesSlice is a Function that returns a slice of the given Bytes value.
|
||||
var BytesSliceFunc = function.New(&function.Spec{
|
||||
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),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
func BytesLen(buf cty.Value) (cty.Value, error) {
|
||||
return BytesLenFunc.Call([]cty.Value{buf})
|
||||
}
|
||||
|
||||
func BytesSlice(buf cty.Value, offset cty.Value, length cty.Value) (cty.Value, error) {
|
||||
return BytesSliceFunc.Call([]cty.Value{buf, offset, length})
|
||||
}
|
1453
vendor/github.com/zclconf/go-cty/cty/function/stdlib/collection.go
generated
vendored
Normal file
1453
vendor/github.com/zclconf/go-cty/cty/function/stdlib/collection.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
87
vendor/github.com/zclconf/go-cty/cty/function/stdlib/conversion.go
generated
vendored
Normal file
87
vendor/github.com/zclconf/go-cty/cty/function/stdlib/conversion.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/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 MakeToFunc(wantTy cty.Type) function.Function {
|
||||
return function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "v",
|
||||
// We use DynamicPseudoType rather than wantTy here so that
|
||||
// all values will pass through the function API verbatim and
|
||||
// we can handle the conversion logic within the Type and
|
||||
// Impl functions. This allows us to customize the error
|
||||
// messages to be more appropriate for an explicit type
|
||||
// conversion, whereas the cty function system produces
|
||||
// messages aimed at _implicit_ type conversions.
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
gotTy := args[0].Type()
|
||||
if gotTy.Equals(wantTy) {
|
||||
return wantTy, nil
|
||||
}
|
||||
conv := convert.GetConversionUnsafe(args[0].Type(), wantTy)
|
||||
if conv == nil {
|
||||
// We'll use some specialized errors for some trickier cases,
|
||||
// but most we can handle in a simple way.
|
||||
switch {
|
||||
case gotTy.IsTupleType() && wantTy.IsTupleType():
|
||||
return cty.NilType, function.NewArgErrorf(0, "incompatible tuple type for conversion: %s", convert.MismatchMessage(gotTy, wantTy))
|
||||
case gotTy.IsObjectType() && wantTy.IsObjectType():
|
||||
return cty.NilType, function.NewArgErrorf(0, "incompatible object type for conversion: %s", convert.MismatchMessage(gotTy, wantTy))
|
||||
default:
|
||||
return cty.NilType, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint())
|
||||
}
|
||||
}
|
||||
// If a conversion is available then everything is fine.
|
||||
return wantTy, nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
// We didn't set "AllowUnknown" on our argument, so it is guaranteed
|
||||
// to be known here but may still be null.
|
||||
ret, err := convert.Convert(args[0], retType)
|
||||
if err != nil {
|
||||
// Because we used GetConversionUnsafe above, conversion can
|
||||
// still potentially fail in here. For example, if the user
|
||||
// asks to convert the string "a" to bool then we'll
|
||||
// optimistically permit it during type checking but fail here
|
||||
// once we note that the value isn't either "true" or "false".
|
||||
gotTy := args[0].Type()
|
||||
switch {
|
||||
case gotTy == cty.String && wantTy == cty.Bool:
|
||||
what := "string"
|
||||
if !args[0].IsNull() {
|
||||
what = strconv.Quote(args[0].AsString())
|
||||
}
|
||||
return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to bool; only the strings "true" or "false" are allowed`, what)
|
||||
case gotTy == cty.String && wantTy == cty.Number:
|
||||
what := "string"
|
||||
if !args[0].IsNull() {
|
||||
what = strconv.Quote(args[0].AsString())
|
||||
}
|
||||
return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to number; given string must be a decimal representation of a number`, what)
|
||||
default:
|
||||
return cty.NilVal, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint())
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
},
|
||||
})
|
||||
}
|
102
vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
generated
vendored
Normal file
102
vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var CSVDecodeFunc = function.New(&function.Spec{
|
||||
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
|
||||
},
|
||||
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)
|
||||
|
||||
// Read the header row first, since that'll tell us which indices
|
||||
// map to which attribute names.
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// 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 CSVDecode(str cty.Value) (cty.Value, error) {
|
||||
return CSVDecodeFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
func csvError(err error) error {
|
||||
switch err := err.(type) {
|
||||
case *csv.ParseError:
|
||||
return fmt.Errorf("CSV parse error on line %d: %w", err.Line, err.Err)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
434
vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go
generated
vendored
Normal file
434
vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go
generated
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var FormatDateFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "format",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "time",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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()
|
||||
|
||||
// The leading byte signals the token type
|
||||
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 {
|
||||
// Must be a single escaped quote, ''
|
||||
buf.WriteByte(esc)
|
||||
} else {
|
||||
// The content (until a closing esc) is printed out verbatim
|
||||
// except that we must un-double any double-esc escapes in
|
||||
// the middle of the string.
|
||||
raw := tok[1 : len(tok)-1]
|
||||
for i := 0; i < len(raw); i++ {
|
||||
buf.WriteByte(raw[i])
|
||||
if raw[i] == esc {
|
||||
i++ // skip the escaped quote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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':
|
||||
// We'll just lean on Go's own formatter for this one, since
|
||||
// the necessary information is unexported.
|
||||
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:
|
||||
// Any other starting character indicates a literal sequence
|
||||
buf.Write(tok)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.StringVal(buf.String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TimeAddFunc is a function that adds a duration to a timestamp, returning a new timestamp.
|
||||
var TimeAddFunc = function.New(&function.Spec{
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// 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 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 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
const esc = '\''
|
||||
|
||||
switch {
|
||||
|
||||
case data[0] == esc:
|
||||
// If we have another quote immediately after then this is a single
|
||||
// escaped escape.
|
||||
if len(data) > 1 && data[1] == esc {
|
||||
return 2, data[:2], nil
|
||||
}
|
||||
|
||||
// Beginning of quoted sequence, so we will seek forward until we find
|
||||
// the closing quote, ignoring escaped quotes along the way.
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc {
|
||||
if (i + 1) == len(data) {
|
||||
if atEOF {
|
||||
// We have a closing quote and are at the end of our input
|
||||
return len(data), data, nil
|
||||
} else {
|
||||
// We need at least one more byte to decide if this is an
|
||||
// escape or a terminator.
|
||||
return 0, nil, nil
|
||||
}
|
||||
}
|
||||
if data[i+1] == esc {
|
||||
i++ // doubled-up quotes are an escape sequence
|
||||
continue
|
||||
}
|
||||
// We've found the closing quote
|
||||
return i + 1, data[:i+1], nil
|
||||
}
|
||||
}
|
||||
// If we fall out here then we need more bytes to find the end,
|
||||
// unless we're already at the end with an unclosed quote.
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
return 0, nil, nil
|
||||
|
||||
case startsDateFormatVerb(data[0]):
|
||||
rep := data[0]
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] != rep {
|
||||
return i, data[:i], nil
|
||||
}
|
||||
}
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
// We need more data to decide if we've found the end
|
||||
return 0, nil, nil
|
||||
|
||||
default:
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc || startsDateFormatVerb(data[i]) {
|
||||
return i, data[:i], nil
|
||||
}
|
||||
}
|
||||
// We might not actually be at the end of a literal sequence,
|
||||
// but that doesn't matter since we'll concat them back together
|
||||
// anyway.
|
||||
return len(data), data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func startsDateFormatVerb(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
|
||||
}
|
||||
|
||||
// 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 TimeAdd(timestamp cty.Value, duration cty.Value) (cty.Value, error) {
|
||||
return TimeAddFunc.Call([]cty.Value{timestamp, duration})
|
||||
}
|
13
vendor/github.com/zclconf/go-cty/cty/function/stdlib/doc.go
generated
vendored
Normal file
13
vendor/github.com/zclconf/go-cty/cty/function/stdlib/doc.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
package stdlib
|
519
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go
generated
vendored
Normal file
519
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go
generated
vendored
Normal file
@ -0,0 +1,519 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/apparentlymart/go-textseg/v13/textseg"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/json"
|
||||
)
|
||||
|
||||
//go:generate ragel -Z format_fsm.rl
|
||||
//go:generate gofmt -w format_fsm.go
|
||||
|
||||
var FormatFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "format",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "args",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowNull: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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.
|
||||
return cty.UnknownVal(cty.String), nil
|
||||
}
|
||||
}
|
||||
str, err := formatFSM(args[0].AsString(), args[1:])
|
||||
return cty.StringVal(str), err
|
||||
},
|
||||
})
|
||||
|
||||
var FormatListFunc = function.New(&function.Spec{
|
||||
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)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
fmtVal := args[0]
|
||||
args = args[1:]
|
||||
|
||||
if len(args) == 0 {
|
||||
// With no arguments, this function is equivalent to Format, but
|
||||
// returning a single-element list result.
|
||||
result, err := Format(fmtVal, args...)
|
||||
return cty.ListVal([]cty.Value{result}), err
|
||||
}
|
||||
|
||||
fmtStr := fmtVal.AsString()
|
||||
|
||||
// Each of our arguments will be dealt with either as an iterator
|
||||
// or as a single value. Iterators are used for sequence-type values
|
||||
// (lists, sets, tuples) while everything else is treated as a
|
||||
// single value. The sequences we iterate over are required to be
|
||||
// all the same length.
|
||||
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()) {
|
||||
// We can't iterate this one at all yet then, so we can't
|
||||
// yet produce a result.
|
||||
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() {
|
||||
// We allowed an unknown tuple value to fall through in
|
||||
// our initial check above so that we'd be able to run
|
||||
// the above error checks against it, but we still can't
|
||||
// iterate it if the checks pass.
|
||||
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 {
|
||||
// If our sequences are all empty then our result must be empty.
|
||||
return cty.ListValEmpty(cty.String), nil
|
||||
}
|
||||
|
||||
if iterLen == -1 {
|
||||
// If we didn't encounter any iterables at all then we're going
|
||||
// to just do one iteration with items from singleVals.
|
||||
iterLen = 1
|
||||
}
|
||||
|
||||
ret := make([]cty.Value, 0, iterLen)
|
||||
fmtArgs := make([]cty.Value, len(iterators))
|
||||
Results:
|
||||
for iterIdx := 0; iterIdx < iterLen; iterIdx++ {
|
||||
|
||||
// Construct our arguments for a single format call
|
||||
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 any of the arguments to this call would be unknown then
|
||||
// this particular result is unknown, but we'll keep going
|
||||
// to see if any other iterations can produce known values.
|
||||
if !fmtArgs[i].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.
|
||||
ret = append(ret, cty.UnknownVal(cty.String))
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// 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 Format(format cty.Value, vals ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, 0, len(vals)+1)
|
||||
args = append(args, format)
|
||||
args = append(args, vals...)
|
||||
return FormatFunc.Call(args)
|
||||
}
|
||||
|
||||
// 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 FormatList(format cty.Value, vals ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, 0, len(vals)+1)
|
||||
args = append(args, format)
|
||||
args = append(args, vals...)
|
||||
return FormatListFunc.Call(args)
|
||||
}
|
||||
|
||||
type formatVerb struct {
|
||||
Raw string
|
||||
Offset int
|
||||
|
||||
ArgNum int
|
||||
Mode rune
|
||||
|
||||
Zero bool
|
||||
Sharp bool
|
||||
Plus bool
|
||||
Minus bool
|
||||
Space bool
|
||||
|
||||
HasPrec bool
|
||||
Prec int
|
||||
|
||||
HasWidth bool
|
||||
Width int
|
||||
}
|
||||
|
||||
// formatAppend is called by formatFSM (generated by format_fsm.rl) for each
|
||||
// formatting sequence that is encountered.
|
||||
func formatAppend(verb *formatVerb, buf *bytes.Buffer, args []cty.Value) error {
|
||||
argIdx := verb.ArgNum - 1
|
||||
if argIdx >= len(args) {
|
||||
return fmt.Errorf(
|
||||
"not enough arguments for %q at %d: need index %d but have %d total",
|
||||
verb.Raw, verb.Offset,
|
||||
verb.ArgNum, len(args),
|
||||
)
|
||||
}
|
||||
arg := args[argIdx]
|
||||
|
||||
if verb.Mode != 'v' && arg.IsNull() {
|
||||
return fmt.Errorf("unsupported value for %q at %d: null value cannot be formatted", verb.Raw, verb.Offset)
|
||||
}
|
||||
|
||||
// Normalize to make some things easier for downstream formatters
|
||||
if !verb.HasWidth {
|
||||
verb.Width = -1
|
||||
}
|
||||
if !verb.HasPrec {
|
||||
verb.Prec = -1
|
||||
}
|
||||
|
||||
// For our first pass we'll ensure the verb is supported and then fan
|
||||
// out to other functions based on what conversion is needed.
|
||||
switch verb.Mode {
|
||||
|
||||
case 'v':
|
||||
return formatAppendAsIs(verb, buf, arg)
|
||||
|
||||
case 't':
|
||||
return formatAppendBool(verb, buf, arg)
|
||||
|
||||
case 'b', 'd', 'o', 'x', 'X', 'e', 'E', 'f', 'g', 'G':
|
||||
return formatAppendNumber(verb, buf, arg)
|
||||
|
||||
case 's', 'q':
|
||||
return formatAppendString(verb, buf, arg)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported format verb %q in %q at offset %d", verb.Mode, verb.Raw, verb.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
func formatAppendAsIs(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
|
||||
if !verb.Sharp && !arg.IsNull() {
|
||||
// Unless the caller overrode it with the sharp flag, we'll try some
|
||||
// specialized formats before we fall back on JSON.
|
||||
switch arg.Type() {
|
||||
case cty.String:
|
||||
fmted := arg.AsString()
|
||||
fmted = formatPadWidth(verb, fmted)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
case cty.Number:
|
||||
bf := arg.AsBigFloat()
|
||||
fmted := bf.Text('g', -1)
|
||||
fmted = formatPadWidth(verb, fmted)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
jb, err := json.Marshal(arg, arg.Type())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
fmted := formatPadWidth(verb, string(jb))
|
||||
buf.WriteString(fmted)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatAppendBool(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
var err error
|
||||
arg, err = convert.Convert(arg, cty.Bool)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
|
||||
if arg.True() {
|
||||
buf.WriteString("true")
|
||||
} else {
|
||||
buf.WriteString("false")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatAppendNumber(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
var err error
|
||||
arg, err = convert.Convert(arg, cty.Number)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
|
||||
switch verb.Mode {
|
||||
case 'b', 'd', 'o', 'x', 'X':
|
||||
return formatAppendInteger(verb, buf, arg)
|
||||
default:
|
||||
bf := arg.AsBigFloat()
|
||||
|
||||
// For floats our format syntax is a subset of Go's, so it's
|
||||
// safe for us to just lean on the existing Go implementation.
|
||||
fmtstr := formatStripIndexSegment(verb.Raw)
|
||||
fmted := fmt.Sprintf(fmtstr, bf)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func formatAppendInteger(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
bf := arg.AsBigFloat()
|
||||
bi, acc := bf.Int(nil)
|
||||
if acc != big.Exact {
|
||||
return fmt.Errorf("unsupported value for %q at %d: an integer is required", verb.Raw, verb.Offset)
|
||||
}
|
||||
|
||||
// For integers our format syntax is a subset of Go's, so it's
|
||||
// safe for us to just lean on the existing Go implementation.
|
||||
fmtstr := formatStripIndexSegment(verb.Raw)
|
||||
fmted := fmt.Sprintf(fmtstr, bi)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatAppendString(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
var err error
|
||||
arg, err = convert.Convert(arg, cty.String)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
|
||||
// We _cannot_ directly use the Go fmt.Sprintf implementation for strings
|
||||
// because it measures widths and precisions in runes rather than grapheme
|
||||
// clusters.
|
||||
|
||||
str := arg.AsString()
|
||||
if verb.Prec > 0 {
|
||||
strB := []byte(str)
|
||||
pos := 0
|
||||
wanted := verb.Prec
|
||||
for i := 0; i < wanted; i++ {
|
||||
next := strB[pos:]
|
||||
if len(next) == 0 {
|
||||
// ran out of characters before we hit our max width
|
||||
break
|
||||
}
|
||||
d, _, _ := textseg.ScanGraphemeClusters(strB[pos:], true)
|
||||
pos += d
|
||||
}
|
||||
str = str[:pos]
|
||||
}
|
||||
|
||||
switch verb.Mode {
|
||||
case 's':
|
||||
fmted := formatPadWidth(verb, str)
|
||||
buf.WriteString(fmted)
|
||||
case 'q':
|
||||
jb, err := json.Marshal(cty.StringVal(str), cty.String)
|
||||
if err != nil {
|
||||
// Should never happen, since we know this is a known, non-null string
|
||||
panic(fmt.Errorf("failed to marshal %#v as JSON: %s", arg, err))
|
||||
}
|
||||
fmted := formatPadWidth(verb, string(jb))
|
||||
buf.WriteString(fmted)
|
||||
default:
|
||||
// Should never happen because formatAppend should've already validated
|
||||
panic(fmt.Errorf("invalid string formatting mode %q", verb.Mode))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatPadWidth(verb *formatVerb, fmted string) string {
|
||||
if verb.Width < 0 {
|
||||
return fmted
|
||||
}
|
||||
|
||||
// Safe to ignore errors because ScanGraphemeClusters cannot produce errors
|
||||
givenLen, _ := textseg.TokenCount([]byte(fmted), textseg.ScanGraphemeClusters)
|
||||
wantLen := verb.Width
|
||||
if givenLen >= wantLen {
|
||||
return fmted
|
||||
}
|
||||
|
||||
padLen := wantLen - givenLen
|
||||
padChar := " "
|
||||
if verb.Zero {
|
||||
padChar = "0"
|
||||
}
|
||||
pads := strings.Repeat(padChar, padLen)
|
||||
|
||||
if verb.Minus {
|
||||
return fmted + pads
|
||||
}
|
||||
return pads + fmted
|
||||
}
|
||||
|
||||
// formatStripIndexSegment strips out any [nnn] segment present in a verb
|
||||
// string so that we can pass it through to Go's fmt.Sprintf with a single
|
||||
// argument. This is used in cases where we're just leaning on Go's formatter
|
||||
// because it's a superset of ours.
|
||||
func formatStripIndexSegment(rawVerb string) string {
|
||||
// We assume the string has already been validated here, since we should
|
||||
// only be using this function with strings that were accepted by our
|
||||
// scanner in formatFSM.
|
||||
start := strings.Index(rawVerb, "[")
|
||||
end := strings.Index(rawVerb, "]")
|
||||
if start == -1 || end == -1 {
|
||||
return rawVerb
|
||||
}
|
||||
|
||||
return rawVerb[:start] + rawVerb[end+1:]
|
||||
}
|
374
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go
generated
vendored
Normal file
374
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go
generated
vendored
Normal file
@ -0,0 +1,374 @@
|
||||
// line 1 "format_fsm.rl"
|
||||
// This file is generated from format_fsm.rl. DO NOT EDIT.
|
||||
|
||||
// line 5 "format_fsm.rl"
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// line 21 "format_fsm.go"
|
||||
var _formatfsm_actions []byte = []byte{
|
||||
0, 1, 0, 1, 1, 1, 2, 1, 4,
|
||||
1, 5, 1, 6, 1, 7, 1, 8,
|
||||
1, 9, 1, 10, 1, 11, 1, 14,
|
||||
1, 16, 1, 17, 1, 18, 2, 3,
|
||||
4, 2, 12, 10, 2, 12, 16, 2,
|
||||
12, 18, 2, 13, 14, 2, 15, 10,
|
||||
2, 15, 18,
|
||||
}
|
||||
|
||||
var _formatfsm_key_offsets []byte = []byte{
|
||||
0, 0, 14, 27, 34, 36, 39, 43,
|
||||
51,
|
||||
}
|
||||
|
||||
var _formatfsm_trans_keys []byte = []byte{
|
||||
32, 35, 37, 43, 45, 46, 48, 91,
|
||||
49, 57, 65, 90, 97, 122, 32, 35,
|
||||
43, 45, 46, 48, 91, 49, 57, 65,
|
||||
90, 97, 122, 91, 48, 57, 65, 90,
|
||||
97, 122, 49, 57, 93, 48, 57, 65,
|
||||
90, 97, 122, 46, 91, 48, 57, 65,
|
||||
90, 97, 122, 37,
|
||||
}
|
||||
|
||||
var _formatfsm_single_lengths []byte = []byte{
|
||||
0, 8, 7, 1, 0, 1, 0, 2,
|
||||
1,
|
||||
}
|
||||
|
||||
var _formatfsm_range_lengths []byte = []byte{
|
||||
0, 3, 3, 3, 1, 1, 2, 3,
|
||||
0,
|
||||
}
|
||||
|
||||
var _formatfsm_index_offsets []byte = []byte{
|
||||
0, 0, 12, 23, 28, 30, 33, 36,
|
||||
42,
|
||||
}
|
||||
|
||||
var _formatfsm_indicies []byte = []byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 10,
|
||||
8, 9, 9, 0, 1, 2, 4, 5,
|
||||
6, 7, 10, 8, 9, 9, 0, 13,
|
||||
11, 12, 12, 0, 14, 0, 15, 14,
|
||||
0, 9, 9, 0, 16, 19, 17, 18,
|
||||
18, 0, 20, 3,
|
||||
}
|
||||
|
||||
var _formatfsm_trans_targs []byte = []byte{
|
||||
0, 2, 2, 8, 2, 2, 3, 2,
|
||||
7, 8, 4, 3, 8, 4, 5, 6,
|
||||
3, 7, 8, 4, 1,
|
||||
}
|
||||
|
||||
var _formatfsm_trans_actions []byte = []byte{
|
||||
7, 17, 9, 3, 15, 13, 25, 11,
|
||||
43, 29, 19, 27, 49, 46, 21, 0,
|
||||
37, 23, 40, 34, 1,
|
||||
}
|
||||
|
||||
var _formatfsm_eof_actions []byte = []byte{
|
||||
0, 31, 31, 31, 31, 31, 31, 31,
|
||||
5,
|
||||
}
|
||||
|
||||
const formatfsm_start int = 8
|
||||
const formatfsm_first_final int = 8
|
||||
const formatfsm_error int = 0
|
||||
|
||||
const formatfsm_en_main int = 8
|
||||
|
||||
// line 20 "format_fsm.rl"
|
||||
|
||||
func formatFSM(format string, a []cty.Value) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
data := format
|
||||
nextArg := 1 // arg numbers are 1-based
|
||||
var verb formatVerb
|
||||
highestArgIdx := 0 // zero means "none", since arg numbers are 1-based
|
||||
|
||||
// line 159 "format_fsm.rl"
|
||||
|
||||
// Ragel state
|
||||
p := 0 // "Pointer" into data
|
||||
pe := len(data) // End-of-data "pointer"
|
||||
cs := 0 // current state (will be initialized by ragel-generated code)
|
||||
ts := 0
|
||||
te := 0
|
||||
eof := pe
|
||||
|
||||
// Keep Go compiler happy even if generated code doesn't use these
|
||||
_ = ts
|
||||
_ = te
|
||||
_ = eof
|
||||
|
||||
// line 123 "format_fsm.go"
|
||||
{
|
||||
cs = formatfsm_start
|
||||
}
|
||||
|
||||
// line 128 "format_fsm.go"
|
||||
{
|
||||
var _klen int
|
||||
var _trans int
|
||||
var _acts int
|
||||
var _nacts uint
|
||||
var _keys int
|
||||
if p == pe {
|
||||
goto _test_eof
|
||||
}
|
||||
if cs == 0 {
|
||||
goto _out
|
||||
}
|
||||
_resume:
|
||||
_keys = int(_formatfsm_key_offsets[cs])
|
||||
_trans = int(_formatfsm_index_offsets[cs])
|
||||
|
||||
_klen = int(_formatfsm_single_lengths[cs])
|
||||
if _klen > 0 {
|
||||
_lower := int(_keys)
|
||||
var _mid int
|
||||
_upper := int(_keys + _klen - 1)
|
||||
for {
|
||||
if _upper < _lower {
|
||||
break
|
||||
}
|
||||
|
||||
_mid = _lower + ((_upper - _lower) >> 1)
|
||||
switch {
|
||||
case data[p] < _formatfsm_trans_keys[_mid]:
|
||||
_upper = _mid - 1
|
||||
case data[p] > _formatfsm_trans_keys[_mid]:
|
||||
_lower = _mid + 1
|
||||
default:
|
||||
_trans += int(_mid - int(_keys))
|
||||
goto _match
|
||||
}
|
||||
}
|
||||
_keys += _klen
|
||||
_trans += _klen
|
||||
}
|
||||
|
||||
_klen = int(_formatfsm_range_lengths[cs])
|
||||
if _klen > 0 {
|
||||
_lower := int(_keys)
|
||||
var _mid int
|
||||
_upper := int(_keys + (_klen << 1) - 2)
|
||||
for {
|
||||
if _upper < _lower {
|
||||
break
|
||||
}
|
||||
|
||||
_mid = _lower + (((_upper - _lower) >> 1) & ^1)
|
||||
switch {
|
||||
case data[p] < _formatfsm_trans_keys[_mid]:
|
||||
_upper = _mid - 2
|
||||
case data[p] > _formatfsm_trans_keys[_mid+1]:
|
||||
_lower = _mid + 2
|
||||
default:
|
||||
_trans += int((_mid - int(_keys)) >> 1)
|
||||
goto _match
|
||||
}
|
||||
}
|
||||
_trans += _klen
|
||||
}
|
||||
|
||||
_match:
|
||||
_trans = int(_formatfsm_indicies[_trans])
|
||||
cs = int(_formatfsm_trans_targs[_trans])
|
||||
|
||||
if _formatfsm_trans_actions[_trans] == 0 {
|
||||
goto _again
|
||||
}
|
||||
|
||||
_acts = int(_formatfsm_trans_actions[_trans])
|
||||
_nacts = uint(_formatfsm_actions[_acts])
|
||||
_acts++
|
||||
for ; _nacts > 0; _nacts-- {
|
||||
_acts++
|
||||
switch _formatfsm_actions[_acts-1] {
|
||||
case 0:
|
||||
// line 31 "format_fsm.rl"
|
||||
|
||||
verb = formatVerb{
|
||||
ArgNum: nextArg,
|
||||
Prec: -1,
|
||||
Width: -1,
|
||||
}
|
||||
ts = p
|
||||
|
||||
case 1:
|
||||
// line 40 "format_fsm.rl"
|
||||
|
||||
buf.WriteByte(data[p])
|
||||
|
||||
case 4:
|
||||
// line 51 "format_fsm.rl"
|
||||
|
||||
// We'll try to slurp a whole UTF-8 sequence here, to give the user
|
||||
// better feedback.
|
||||
r, _ := utf8.DecodeRuneInString(data[p:])
|
||||
return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p)
|
||||
|
||||
case 5:
|
||||
// line 58 "format_fsm.rl"
|
||||
|
||||
verb.Sharp = true
|
||||
|
||||
case 6:
|
||||
// line 61 "format_fsm.rl"
|
||||
|
||||
verb.Zero = true
|
||||
|
||||
case 7:
|
||||
// line 64 "format_fsm.rl"
|
||||
|
||||
verb.Minus = true
|
||||
|
||||
case 8:
|
||||
// line 67 "format_fsm.rl"
|
||||
|
||||
verb.Plus = true
|
||||
|
||||
case 9:
|
||||
// line 70 "format_fsm.rl"
|
||||
|
||||
verb.Space = true
|
||||
|
||||
case 10:
|
||||
// line 74 "format_fsm.rl"
|
||||
|
||||
verb.ArgNum = 0
|
||||
|
||||
case 11:
|
||||
// line 77 "format_fsm.rl"
|
||||
|
||||
verb.ArgNum = (10 * verb.ArgNum) + (int(data[p]) - '0')
|
||||
|
||||
case 12:
|
||||
// line 81 "format_fsm.rl"
|
||||
|
||||
verb.HasWidth = true
|
||||
|
||||
case 13:
|
||||
// line 84 "format_fsm.rl"
|
||||
|
||||
verb.Width = 0
|
||||
|
||||
case 14:
|
||||
// line 87 "format_fsm.rl"
|
||||
|
||||
verb.Width = (10 * verb.Width) + (int(data[p]) - '0')
|
||||
|
||||
case 15:
|
||||
// line 91 "format_fsm.rl"
|
||||
|
||||
verb.HasPrec = true
|
||||
|
||||
case 16:
|
||||
// line 94 "format_fsm.rl"
|
||||
|
||||
verb.Prec = 0
|
||||
|
||||
case 17:
|
||||
// line 97 "format_fsm.rl"
|
||||
|
||||
verb.Prec = (10 * verb.Prec) + (int(data[p]) - '0')
|
||||
|
||||
case 18:
|
||||
// line 101 "format_fsm.rl"
|
||||
|
||||
verb.Mode = rune(data[p])
|
||||
te = p + 1
|
||||
verb.Raw = data[ts:te]
|
||||
verb.Offset = ts
|
||||
|
||||
if verb.ArgNum > highestArgIdx {
|
||||
highestArgIdx = verb.ArgNum
|
||||
}
|
||||
|
||||
err := formatAppend(&verb, &buf, a)
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
nextArg = verb.ArgNum + 1
|
||||
|
||||
// line 330 "format_fsm.go"
|
||||
}
|
||||
}
|
||||
|
||||
_again:
|
||||
if cs == 0 {
|
||||
goto _out
|
||||
}
|
||||
p++
|
||||
if p != pe {
|
||||
goto _resume
|
||||
}
|
||||
_test_eof:
|
||||
{
|
||||
}
|
||||
if p == eof {
|
||||
__acts := _formatfsm_eof_actions[cs]
|
||||
__nacts := uint(_formatfsm_actions[__acts])
|
||||
__acts++
|
||||
for ; __nacts > 0; __nacts-- {
|
||||
__acts++
|
||||
switch _formatfsm_actions[__acts-1] {
|
||||
case 2:
|
||||
// line 44 "format_fsm.rl"
|
||||
|
||||
case 3:
|
||||
// line 47 "format_fsm.rl"
|
||||
|
||||
return buf.String(), fmt.Errorf("invalid format string starting at offset %d", p)
|
||||
|
||||
case 4:
|
||||
// line 51 "format_fsm.rl"
|
||||
|
||||
// We'll try to slurp a whole UTF-8 sequence here, to give the user
|
||||
// better feedback.
|
||||
r, _ := utf8.DecodeRuneInString(data[p:])
|
||||
return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p)
|
||||
|
||||
// line 369 "format_fsm.go"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_out:
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// line 177 "format_fsm.rl"
|
||||
|
||||
// If we fall out here without being in a final state then we've
|
||||
// encountered something that the scanner can't match, which should
|
||||
// be impossible (the scanner matches all bytes _somehow_) but we'll
|
||||
// flag it anyway rather than just losing data from the end.
|
||||
if cs < formatfsm_first_final {
|
||||
return buf.String(), fmt.Errorf("extraneous characters beginning at offset %d", p)
|
||||
}
|
||||
|
||||
if highestArgIdx < len(a) {
|
||||
// Extraneous args are an error, to more easily detect mistakes
|
||||
firstBad := highestArgIdx + 1
|
||||
if highestArgIdx == 0 {
|
||||
// Custom error message for this case
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; no verbs in format string")
|
||||
}
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; only %d used by format string", highestArgIdx)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
198
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl
generated
vendored
Normal file
198
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
// This file is generated from format_fsm.rl. DO NOT EDIT.
|
||||
%%{
|
||||
# (except you are actually in scan_tokens.rl here, so edit away!)
|
||||
machine formatfsm;
|
||||
}%%
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
%%{
|
||||
write data;
|
||||
}%%
|
||||
|
||||
func formatFSM(format string, a []cty.Value) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
data := format
|
||||
nextArg := 1 // arg numbers are 1-based
|
||||
var verb formatVerb
|
||||
highestArgIdx := 0 // zero means "none", since arg numbers are 1-based
|
||||
|
||||
%%{
|
||||
|
||||
action begin {
|
||||
verb = formatVerb{
|
||||
ArgNum: nextArg,
|
||||
Prec: -1,
|
||||
Width: -1,
|
||||
}
|
||||
ts = p
|
||||
}
|
||||
|
||||
action emit {
|
||||
buf.WriteByte(fc);
|
||||
}
|
||||
|
||||
action finish_ok {
|
||||
}
|
||||
|
||||
action finish_err {
|
||||
return buf.String(), fmt.Errorf("invalid format string starting at offset %d", p)
|
||||
}
|
||||
|
||||
action err_char {
|
||||
// We'll try to slurp a whole UTF-8 sequence here, to give the user
|
||||
// better feedback.
|
||||
r, _ := utf8.DecodeRuneInString(data[p:])
|
||||
return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p)
|
||||
}
|
||||
|
||||
action flag_sharp {
|
||||
verb.Sharp = true
|
||||
}
|
||||
action flag_zero {
|
||||
verb.Zero = true
|
||||
}
|
||||
action flag_minus {
|
||||
verb.Minus = true
|
||||
}
|
||||
action flag_plus {
|
||||
verb.Plus = true
|
||||
}
|
||||
action flag_space {
|
||||
verb.Space = true
|
||||
}
|
||||
|
||||
action argidx_reset {
|
||||
verb.ArgNum = 0
|
||||
}
|
||||
action argidx_num {
|
||||
verb.ArgNum = (10 * verb.ArgNum) + (int(fc) - '0')
|
||||
}
|
||||
|
||||
action has_width {
|
||||
verb.HasWidth = true
|
||||
}
|
||||
action width_reset {
|
||||
verb.Width = 0
|
||||
}
|
||||
action width_num {
|
||||
verb.Width = (10 * verb.Width) + (int(fc) - '0')
|
||||
}
|
||||
|
||||
action has_prec {
|
||||
verb.HasPrec = true
|
||||
}
|
||||
action prec_reset {
|
||||
verb.Prec = 0
|
||||
}
|
||||
action prec_num {
|
||||
verb.Prec = (10 * verb.Prec) + (int(fc) - '0')
|
||||
}
|
||||
|
||||
action mode {
|
||||
verb.Mode = rune(fc)
|
||||
te = p+1
|
||||
verb.Raw = data[ts:te]
|
||||
verb.Offset = ts
|
||||
|
||||
if verb.ArgNum > highestArgIdx {
|
||||
highestArgIdx = verb.ArgNum
|
||||
}
|
||||
|
||||
err := formatAppend(&verb, &buf, a)
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
nextArg = verb.ArgNum + 1
|
||||
}
|
||||
|
||||
# a number that isn't zero and doesn't have a leading zero
|
||||
num = [1-9] [0-9]*;
|
||||
|
||||
flags = (
|
||||
'0' @flag_zero |
|
||||
'#' @flag_sharp |
|
||||
'-' @flag_minus |
|
||||
'+' @flag_plus |
|
||||
' ' @flag_space
|
||||
)*;
|
||||
|
||||
argidx = ((
|
||||
'[' (num $argidx_num) ']'
|
||||
) >argidx_reset)?;
|
||||
|
||||
width = (
|
||||
( num $width_num ) >width_reset %has_width
|
||||
)?;
|
||||
|
||||
precision = (
|
||||
('.' ( digit* $prec_num )) >prec_reset %has_prec
|
||||
)?;
|
||||
|
||||
# We accept any letter here, but will be more picky in formatAppend
|
||||
mode = ('a'..'z' | 'A'..'Z') @mode;
|
||||
|
||||
fmt_verb = (
|
||||
'%' @begin
|
||||
flags
|
||||
width
|
||||
precision
|
||||
argidx
|
||||
mode
|
||||
);
|
||||
|
||||
main := (
|
||||
[^%] @emit |
|
||||
'%%' @emit |
|
||||
fmt_verb
|
||||
)* @/finish_err %/finish_ok $!err_char;
|
||||
|
||||
}%%
|
||||
|
||||
// Ragel state
|
||||
p := 0 // "Pointer" into data
|
||||
pe := len(data) // End-of-data "pointer"
|
||||
cs := 0 // current state (will be initialized by ragel-generated code)
|
||||
ts := 0
|
||||
te := 0
|
||||
eof := pe
|
||||
|
||||
// Keep Go compiler happy even if generated code doesn't use these
|
||||
_ = ts
|
||||
_ = te
|
||||
_ = eof
|
||||
|
||||
%%{
|
||||
write init;
|
||||
write exec;
|
||||
}%%
|
||||
|
||||
// If we fall out here without being in a final state then we've
|
||||
// encountered something that the scanner can't match, which should
|
||||
// be impossible (the scanner matches all bytes _somehow_) but we'll
|
||||
// flag it anyway rather than just losing data from the end.
|
||||
if cs < formatfsm_first_final {
|
||||
return buf.String(), fmt.Errorf("extraneous characters beginning at offset %d", p)
|
||||
}
|
||||
|
||||
if highestArgIdx < len(a) {
|
||||
// Extraneous args are an error, to more easily detect mistakes
|
||||
firstBad := highestArgIdx+1
|
||||
if highestArgIdx == 0 {
|
||||
// Custom error message for this case
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; no verbs in format string")
|
||||
}
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; only %d used by format string", highestArgIdx)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
107
vendor/github.com/zclconf/go-cty/cty/function/stdlib/general.go
generated
vendored
Normal file
107
vendor/github.com/zclconf/go-cty/cty/function/stdlib/general.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var EqualFunc = function.New(&function.Spec{
|
||||
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),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].Equals(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var NotEqualFunc = function.New(&function.Spec{
|
||||
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),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].Equals(args[1]).Not(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var CoalesceFunc = function.New(&function.Spec{
|
||||
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
|
||||
},
|
||||
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")
|
||||
},
|
||||
})
|
||||
|
||||
// Equal determines whether the two given values are equal, returning a
|
||||
// bool value.
|
||||
func Equal(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return EqualFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// NotEqual is the opposite of Equal.
|
||||
func NotEqual(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return NotEqualFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Coalesce returns the first of the given arguments that is not null. If
|
||||
// all arguments are null, an error is produced.
|
||||
func Coalesce(vals ...cty.Value) (cty.Value, error) {
|
||||
return CoalesceFunc.Call(vals)
|
||||
}
|
77
vendor/github.com/zclconf/go-cty/cty/function/stdlib/json.go
generated
vendored
Normal file
77
vendor/github.com/zclconf/go-cty/cty/function/stdlib/json.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/json"
|
||||
)
|
||||
|
||||
var JSONEncodeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "val",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
}
|
||||
|
||||
if val.IsNull() {
|
||||
return cty.StringVal("null"), nil
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(val, val.Type())
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
return cty.StringVal(string(buf)), nil
|
||||
},
|
||||
})
|
||||
|
||||
var JSONDecodeFunc = function.New(&function.Spec{
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
},
|
||||
})
|
||||
|
||||
// JSONEncode returns a JSON serialization of the given value.
|
||||
func JSONEncode(val cty.Value) (cty.Value, error) {
|
||||
return JSONEncodeFunc.Call([]cty.Value{val})
|
||||
}
|
||||
|
||||
// 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 JSONDecode(str cty.Value) (cty.Value, error) {
|
||||
return JSONDecodeFunc.Call([]cty.Value{str})
|
||||
}
|
667
vendor/github.com/zclconf/go-cty/cty/function/stdlib/number.go
generated
vendored
Normal file
667
vendor/github.com/zclconf/go-cty/cty/function/stdlib/number.go
generated
vendored
Normal file
@ -0,0 +1,667 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
var AbsoluteFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Absolute(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var AddFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
// the cty Function abstraction.
|
||||
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 {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return args[0].Add(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SubtractFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
// the cty Function abstraction.
|
||||
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 {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return args[0].Subtract(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var MultiplyFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
// the cty Function abstraction.
|
||||
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 {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return args[0].Multiply(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var DivideFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
// the cty Function abstraction.
|
||||
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 {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return args[0].Divide(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var ModuloFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
// the cty Function abstraction.
|
||||
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 {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return args[0].Modulo(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var GreaterThanFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].GreaterThan(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var GreaterThanOrEqualToFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].GreaterThanOrEqualTo(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var LessThanFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].LessThan(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var LessThanOrEqualToFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].LessThanOrEqualTo(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var NegateFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Negate(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var MinFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "numbers",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var MaxFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "numbers",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var IntFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// CeilFunc is a function that returns the closest whole number greater
|
||||
// than or equal to the given value.
|
||||
var CeilFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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:
|
||||
// Done.
|
||||
case big.Below:
|
||||
i.Add(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.
|
||||
var FloorFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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:
|
||||
// Done.
|
||||
case big.Above:
|
||||
i.Sub(i, big.NewInt(1))
|
||||
}
|
||||
|
||||
return cty.NumberVal(f.SetInt(i)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// LogFunc is a function that returns the logarithm of a given number in a given base.
|
||||
var LogFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "base",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// PowFunc is a function that returns the logarithm of a given number in a given base.
|
||||
var PowFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "power",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// SignumFunc is a function that determines the sign of a number, returning a
|
||||
// number between -1 and 1 to represent the sign..
|
||||
var SignumFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// ParseIntFunc is a function that parses a string argument and returns an integer of the specified base.
|
||||
var ParseIntFunc = function.New(&function.Spec{
|
||||
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
|
||||
},
|
||||
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// Absolute returns the magnitude of the given number, without its sign.
|
||||
// That is, it turns negative values into positive values.
|
||||
func Absolute(num cty.Value) (cty.Value, error) {
|
||||
return AbsoluteFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Add returns the sum of the two given numbers.
|
||||
func Add(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return AddFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Subtract returns the difference between the two given numbers.
|
||||
func Subtract(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return SubtractFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Multiply returns the product of the two given numbers.
|
||||
func Multiply(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return MultiplyFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Divide returns a divided by b, where both a and b are numbers.
|
||||
func Divide(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return DivideFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Negate returns the given number multipled by -1.
|
||||
func Negate(num cty.Value) (cty.Value, error) {
|
||||
return NegateFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// LessThan returns true if a is less than b.
|
||||
func LessThan(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return LessThanFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// LessThanOrEqualTo returns true if a is less than b.
|
||||
func LessThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return LessThanOrEqualToFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// GreaterThan returns true if a is less than b.
|
||||
func GreaterThan(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return GreaterThanFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// GreaterThanOrEqualTo returns true if a is less than b.
|
||||
func GreaterThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return GreaterThanOrEqualToFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Modulo returns the remainder of a divided by b under integer division,
|
||||
// where both a and b are numbers.
|
||||
func Modulo(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return ModuloFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Min returns the minimum number from the given numbers.
|
||||
func Min(numbers ...cty.Value) (cty.Value, error) {
|
||||
return MinFunc.Call(numbers)
|
||||
}
|
||||
|
||||
// Max returns the maximum number from the given numbers.
|
||||
func Max(numbers ...cty.Value) (cty.Value, error) {
|
||||
return MaxFunc.Call(numbers)
|
||||
}
|
||||
|
||||
// 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 Int(num cty.Value) (cty.Value, error) {
|
||||
if num == cty.PositiveInfinity || num == cty.NegativeInfinity {
|
||||
return cty.NilVal, fmt.Errorf("can't truncate infinity to an integer")
|
||||
}
|
||||
return IntFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Ceil returns the closest whole number greater than or equal to the given value.
|
||||
func Ceil(num cty.Value) (cty.Value, error) {
|
||||
return CeilFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Floor returns the closest whole number lesser than or equal to the given value.
|
||||
func Floor(num cty.Value) (cty.Value, error) {
|
||||
return FloorFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Log returns returns the logarithm of a given number in a given base.
|
||||
func Log(num, base cty.Value) (cty.Value, error) {
|
||||
return LogFunc.Call([]cty.Value{num, base})
|
||||
}
|
||||
|
||||
// Pow returns the logarithm of a given number in a given base.
|
||||
func Pow(num, power cty.Value) (cty.Value, error) {
|
||||
return PowFunc.Call([]cty.Value{num, power})
|
||||
}
|
||||
|
||||
// Signum determines the sign of a number, returning a number between -1 and
|
||||
// 1 to represent the sign.
|
||||
func Signum(num cty.Value) (cty.Value, error) {
|
||||
return SignumFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// ParseInt parses a string argument and returns an integer of the specified base.
|
||||
func ParseInt(num cty.Value, base cty.Value) (cty.Value, error) {
|
||||
return ParseIntFunc.Call([]cty.Value{num, base})
|
||||
}
|
233
vendor/github.com/zclconf/go-cty/cty/function/stdlib/regexp.go
generated
vendored
Normal file
233
vendor/github.com/zclconf/go-cty/cty/function/stdlib/regexp.go
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
resyntax "regexp/syntax"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var RegexFunc = function.New(&function.Spec{
|
||||
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() {
|
||||
// We can't predict our type without seeing our pattern
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
retTy, err := regexPatternResultType(args[0].AsString())
|
||||
if err != nil {
|
||||
err = function.NewArgError(0, err)
|
||||
}
|
||||
return retTy, err
|
||||
},
|
||||
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 {
|
||||
// Should never happen, since we checked this in the Type function above.
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var RegexAllFunc = function.New(&function.Spec{
|
||||
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() {
|
||||
// We can't predict our type without seeing our pattern,
|
||||
// but we do know it'll always be a list of something.
|
||||
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
|
||||
},
|
||||
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 {
|
||||
// Should never happen, since we checked this in the Type function above.
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// 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 Regex(pattern, str cty.Value) (cty.Value, error) {
|
||||
return RegexFunc.Call([]cty.Value{pattern, str})
|
||||
}
|
||||
|
||||
// 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 RegexAll(pattern, str cty.Value) (cty.Value, error) {
|
||||
return RegexAllFunc.Call([]cty.Value{pattern, str})
|
||||
}
|
||||
|
||||
// regexPatternResultType parses the given regular expression pattern and
|
||||
// returns the structural type that would be returned to represent its
|
||||
// capture groups.
|
||||
//
|
||||
// Returns an error if parsing fails or if the pattern uses a mixture of
|
||||
// named and unnamed capture groups, which is not permitted.
|
||||
func regexPatternResultType(pattern string) (cty.Type, error) {
|
||||
re, rawErr := regexp.Compile(pattern)
|
||||
switch err := rawErr.(type) {
|
||||
case *resyntax.Error:
|
||||
return cty.NilType, fmt.Errorf("invalid regexp pattern: %s in %s", err.Code, err.Expr)
|
||||
case error:
|
||||
// Should never happen, since all regexp compile errors should
|
||||
// be resyntax.Error, but just in case...
|
||||
return cty.NilType, fmt.Errorf("error parsing pattern: %s", err)
|
||||
}
|
||||
|
||||
allNames := re.SubexpNames()[1:]
|
||||
var names []string
|
||||
unnamed := 0
|
||||
for _, name := range allNames {
|
||||
if name == "" {
|
||||
unnamed++
|
||||
} else {
|
||||
if names == nil {
|
||||
names = make([]string, 0, len(allNames))
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case unnamed == 0 && len(names) == 0:
|
||||
// If there are no capture groups at all then we'll return just a
|
||||
// single string for the whole match.
|
||||
return cty.String, nil
|
||||
case unnamed > 0 && len(names) > 0:
|
||||
return cty.NilType, fmt.Errorf("invalid regexp pattern: cannot mix both named and unnamed capture groups")
|
||||
case unnamed > 0:
|
||||
// For unnamed captures, we return a tuple of them all in order.
|
||||
etys := make([]cty.Type, unnamed)
|
||||
for i := range etys {
|
||||
etys[i] = cty.String
|
||||
}
|
||||
return cty.Tuple(etys), nil
|
||||
default:
|
||||
// For named captures, we return an object using the capture names
|
||||
// as keys.
|
||||
atys := make(map[string]cty.Type, len(names))
|
||||
for _, name := range names {
|
||||
atys[name] = cty.String
|
||||
}
|
||||
return cty.Object(atys), nil
|
||||
}
|
||||
}
|
||||
|
||||
func regexPatternResult(re *regexp.Regexp, str string, captureIdxs []int, retType cty.Type) cty.Value {
|
||||
switch {
|
||||
case retType == cty.String:
|
||||
start, end := captureIdxs[0], captureIdxs[1]
|
||||
return cty.StringVal(str[start:end])
|
||||
case retType.IsTupleType():
|
||||
captureIdxs = captureIdxs[2:] // index 0 is the whole pattern span, which we ignore by skipping one pair
|
||||
vals := make([]cty.Value, len(captureIdxs)/2)
|
||||
for i := range vals {
|
||||
start, end := captureIdxs[i*2], captureIdxs[i*2+1]
|
||||
if start < 0 || end < 0 {
|
||||
vals[i] = cty.NullVal(cty.String) // Did not match anything because containing group didn't match
|
||||
continue
|
||||
}
|
||||
vals[i] = cty.StringVal(str[start:end])
|
||||
}
|
||||
return cty.TupleVal(vals)
|
||||
case retType.IsObjectType():
|
||||
captureIdxs = captureIdxs[2:] // index 0 is the whole pattern span, which we ignore by skipping one pair
|
||||
vals := make(map[string]cty.Value, len(captureIdxs)/2)
|
||||
names := re.SubexpNames()[1:]
|
||||
for i, name := range names {
|
||||
start, end := captureIdxs[i*2], captureIdxs[i*2+1]
|
||||
if start < 0 || end < 0 {
|
||||
vals[name] = cty.NullVal(cty.String) // Did not match anything because containing group didn't match
|
||||
continue
|
||||
}
|
||||
vals[name] = cty.StringVal(str[start:end])
|
||||
}
|
||||
return cty.ObjectVal(vals)
|
||||
default:
|
||||
// Should never happen
|
||||
panic(fmt.Sprintf("invalid return type %#v", retType))
|
||||
}
|
||||
}
|
235
vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go
generated
vendored
Normal file
235
vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go
generated
vendored
Normal file
@ -0,0 +1,235 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var ConcatFunc = function.New(&function.Spec{
|
||||
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() {
|
||||
// Possibly we're going to return a list, if all of our other
|
||||
// args are also lists and we can find a common element type.
|
||||
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 {
|
||||
// Discard marks for nested values, as we only need to handle types
|
||||
// and lengths.
|
||||
val, _ := val.UnmarkDeep()
|
||||
|
||||
ety := val.Type()
|
||||
switch {
|
||||
case ety.IsTupleType():
|
||||
etys = append(etys, ety.TupleElementTypes()...)
|
||||
case ety.IsListType():
|
||||
if !val.IsKnown() {
|
||||
// We need to know the list to count its elements to
|
||||
// build our tuple type, so any concat of an unknown
|
||||
// list can't be typed yet.
|
||||
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
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
switch {
|
||||
case retType.IsListType():
|
||||
// If retType is a list type then we know that all of the
|
||||
// given values will be lists and that they will either be of
|
||||
// retType or of something we can convert to retType.
|
||||
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 {
|
||||
// Conversion might fail because we used UnifyUnsafe
|
||||
// to choose our return type.
|
||||
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():
|
||||
// If retType is a tuple type then we could have a mixture of
|
||||
// lists and tuples but we know they all have known values
|
||||
// (because our params don't AllowUnknown) and we know that
|
||||
// concatenating them all together will produce a tuple of
|
||||
// retType because of the work we did in the Type function above.
|
||||
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)
|
||||
}
|
||||
|
||||
// Both lists and tuples support ElementIterator, so this is easy.
|
||||
it := seq.ElementIterator()
|
||||
for it.Next() {
|
||||
_, v := it.Element()
|
||||
vals = append(vals, v)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.TupleVal(vals).WithMarks(markses...), nil
|
||||
default:
|
||||
// should never happen if Type is working correctly above
|
||||
panic("unsupported return type")
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
var RangeFunc = function.New(&function.Spec{
|
||||
VarParam: &function.Parameter{
|
||||
Name: "params",
|
||||
Type: cty.Number,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.Number)),
|
||||
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 {
|
||||
// Artificial limit to prevent bad arguments from consuming huge amounts of memory
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// 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 Concat(seqs ...cty.Value) (cty.Value, error) {
|
||||
return ConcatFunc.Call(seqs)
|
||||
}
|
||||
|
||||
// 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 Range(params ...cty.Value) (cty.Value, error) {
|
||||
return RangeFunc.Call(params)
|
||||
}
|
222
vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go
generated
vendored
Normal file
222
vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var SetHasElementFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "set",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "elem",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].HasElement(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SetUnionFunc = function.New(&function.Spec{
|
||||
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,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Union(s2)
|
||||
}, true),
|
||||
})
|
||||
|
||||
var SetIntersectionFunc = function.New(&function.Spec{
|
||||
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,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Intersection(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSubtractFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Subtract(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
||||
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,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.SymmetricDifference(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
// SetHasElement determines whether the given set contains the given value as an
|
||||
// element.
|
||||
func SetHasElement(set cty.Value, elem cty.Value) (cty.Value, error) {
|
||||
return SetHasElementFunc.Call([]cty.Value{set, elem})
|
||||
}
|
||||
|
||||
// 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 SetUnion(sets ...cty.Value) (cty.Value, error) {
|
||||
return SetUnionFunc.Call(sets)
|
||||
}
|
||||
|
||||
// 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 SetIntersection(sets ...cty.Value) (cty.Value, error) {
|
||||
return SetIntersectionFunc.Call(sets)
|
||||
}
|
||||
|
||||
// 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 SetSubtract(a, b cty.Value) (cty.Value, error) {
|
||||
return SetSubtractFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// 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 SetSymmetricDifference(sets ...cty.Value) (cty.Value, error) {
|
||||
return SetSymmetricDifferenceFunc.Call(sets)
|
||||
}
|
||||
|
||||
func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) {
|
||||
var etys []cty.Type
|
||||
for _, arg := range args {
|
||||
ty := arg.Type().ElementType()
|
||||
|
||||
// Do not unify types for empty dynamic pseudo typed collections. These
|
||||
// will always convert to any other concrete type.
|
||||
if arg.IsKnown() && arg.LengthInt() == 0 && ty.Equals(cty.DynamicPseudoType) {
|
||||
continue
|
||||
}
|
||||
|
||||
etys = append(etys, ty)
|
||||
}
|
||||
|
||||
// If all element types were skipped (due to being empty dynamic collections),
|
||||
// the return type should also be a set of dynamic pseudo type.
|
||||
if len(etys) == 0 {
|
||||
return cty.Set(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
newEty, _ := convert.UnifyUnsafe(etys)
|
||||
if newEty == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("given sets must all have compatible element types")
|
||||
}
|
||||
return cty.Set(newEty), nil
|
||||
}
|
||||
|
||||
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet, allowUnknowns bool) function.ImplFunc {
|
||||
return func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
first := args[0]
|
||||
first, err = convert.Convert(first, retType)
|
||||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(0, err)
|
||||
}
|
||||
if !allowUnknowns && !first.IsWhollyKnown() {
|
||||
// This set function can produce a correct result only when all
|
||||
// elements are known, because eventually knowing the unknown
|
||||
// values may cause the result to have fewer known elements, or
|
||||
// might cause a result with no unknown elements at all to become
|
||||
// one with a different length.
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
set := first.AsValueSet()
|
||||
for i, arg := range args[1:] {
|
||||
arg, err := convert.Convert(arg, retType)
|
||||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(i+1, err)
|
||||
}
|
||||
if !allowUnknowns && !arg.IsWhollyKnown() {
|
||||
// (For the same reason as we did this check for "first" above.)
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
argSet := arg.AsValueSet()
|
||||
set = f(set, argSet)
|
||||
}
|
||||
return cty.SetValFromValueSet(set), nil
|
||||
}
|
||||
}
|
546
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string.go
generated
vendored
Normal file
546
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string.go
generated
vendored
Normal file
@ -0,0 +1,546 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/apparentlymart/go-textseg/v13/textseg"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
var UpperFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
in := args[0].AsString()
|
||||
out := strings.ToUpper(in)
|
||||
return cty.StringVal(out), nil
|
||||
},
|
||||
})
|
||||
|
||||
var LowerFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
in := args[0].AsString()
|
||||
out := strings.ToLower(in)
|
||||
return cty.StringVal(out), nil
|
||||
},
|
||||
})
|
||||
|
||||
var ReverseFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var StrlenFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
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
|
||||
}
|
||||
|
||||
return cty.NumberIntVal(int64(l)), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SubstrFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "offset",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "length",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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 {
|
||||
// should never happen
|
||||
panic("Stdlen returned an error")
|
||||
}
|
||||
|
||||
var totalLen int
|
||||
err = gocty.FromCtyValue(totalLenNum, &totalLen)
|
||||
if err != nil {
|
||||
// should never happen
|
||||
panic("Stdlen returned a non-int number")
|
||||
}
|
||||
|
||||
offset += totalLen
|
||||
} else if length == 0 {
|
||||
// Short circuit here, after error checks, because if a
|
||||
// string of length 0 has been requested it will always
|
||||
// be the empty string
|
||||
return cty.StringVal(""), nil
|
||||
}
|
||||
|
||||
sub := in
|
||||
pos := 0
|
||||
var i int
|
||||
|
||||
// First we'll seek forward to our offset
|
||||
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 {
|
||||
// Taking the remainder of the string is a fast path since
|
||||
// we can just return the rest of the buffer verbatim.
|
||||
return cty.StringVal(string(sub)), nil
|
||||
}
|
||||
|
||||
// Otherwise we need to start seeking forward again until we
|
||||
// reach the length we want.
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var JoinFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "separator",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "lists",
|
||||
Type: cty.List(cty.String),
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var SortFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "list",
|
||||
Type: cty.List(cty.String),
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.String)),
|
||||
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
|
||||
}
|
||||
if listVal.LengthInt() == 0 { // Easy path
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var SplitFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "separator",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.String)),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// ChompFunc is a function that removes newline characters at the end of a
|
||||
// string.
|
||||
var ChompFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// 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.
|
||||
var IndentFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "spaces",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// TitleFunc is a function that converts the first letter of each word in the
|
||||
// given string to uppercase.
|
||||
var TitleFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return cty.StringVal(strings.Title(args[0].AsString())), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TrimSpaceFunc is a function that removes any space characters from the start
|
||||
// and end of the given string.
|
||||
var TrimSpaceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return cty.StringVal(strings.TrimSpace(args[0].AsString())), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TrimFunc is a function that removes the specified characters from the start
|
||||
// and end of the given string.
|
||||
var TrimFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "cutset",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// TrimPrefixFunc is a function that removes the specified characters from the
|
||||
// start the given string.
|
||||
var TrimPrefixFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "prefix",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// TrimSuffixFunc is a function that removes the specified characters from the
|
||||
// end of the given string.
|
||||
var TrimSuffixFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "suffix",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// Upper is a Function that converts a given string to uppercase.
|
||||
func Upper(str cty.Value) (cty.Value, error) {
|
||||
return UpperFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Lower is a Function that converts a given string to lowercase.
|
||||
func Lower(str cty.Value) (cty.Value, error) {
|
||||
return LowerFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// 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 Reverse(str cty.Value) (cty.Value, error) {
|
||||
return ReverseFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// 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 Strlen(str cty.Value) (cty.Value, error) {
|
||||
return StrlenFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// 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 Substr(str cty.Value, offset cty.Value, length cty.Value) (cty.Value, error) {
|
||||
return SubstrFunc.Call([]cty.Value{str, offset, length})
|
||||
}
|
||||
|
||||
// Join concatenates together the string elements of one or more lists with a
|
||||
// given separator.
|
||||
func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, len(lists)+1)
|
||||
args[0] = sep
|
||||
copy(args[1:], lists)
|
||||
return JoinFunc.Call(args)
|
||||
}
|
||||
|
||||
// Sort re-orders the elements of a given list of strings so that they are
|
||||
// in ascending lexicographical order.
|
||||
func Sort(list cty.Value) (cty.Value, error) {
|
||||
return SortFunc.Call([]cty.Value{list})
|
||||
}
|
||||
|
||||
// Split divides a given string by a given separator, returning a list of
|
||||
// strings containing the characters between the separator sequences.
|
||||
func Split(sep, str cty.Value) (cty.Value, error) {
|
||||
return SplitFunc.Call([]cty.Value{sep, str})
|
||||
}
|
||||
|
||||
// Chomp removes newline characters at the end of a string.
|
||||
func Chomp(str cty.Value) (cty.Value, error) {
|
||||
return ChompFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Indent adds a given number of spaces to the beginnings of all but the first
|
||||
// line in a given multi-line string.
|
||||
func Indent(spaces, str cty.Value) (cty.Value, error) {
|
||||
return IndentFunc.Call([]cty.Value{spaces, str})
|
||||
}
|
||||
|
||||
// Title converts the first letter of each word in the given string to uppercase.
|
||||
func Title(str cty.Value) (cty.Value, error) {
|
||||
return TitleFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// TrimSpace removes any space characters from the start and end of the given string.
|
||||
func TrimSpace(str cty.Value) (cty.Value, error) {
|
||||
return TrimSpaceFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Trim removes the specified characters from the start and end of the given string.
|
||||
func Trim(str, cutset cty.Value) (cty.Value, error) {
|
||||
return TrimFunc.Call([]cty.Value{str, cutset})
|
||||
}
|
||||
|
||||
// TrimPrefix removes the specified prefix from the start of the given string.
|
||||
func TrimPrefix(str, prefix cty.Value) (cty.Value, error) {
|
||||
return TrimPrefixFunc.Call([]cty.Value{str, prefix})
|
||||
}
|
||||
|
||||
// TrimSuffix removes the specified suffix from the end of the given string.
|
||||
func TrimSuffix(str, suffix cty.Value) (cty.Value, error) {
|
||||
return TrimSuffixFunc.Call([]cty.Value{str, suffix})
|
||||
}
|
80
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string_replace.go
generated
vendored
Normal file
80
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string_replace.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// 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.
|
||||
var ReplaceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "substr",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "replace",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// 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.
|
||||
var RegexReplaceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "substr",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "replace",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// Replace searches a given string for another given substring,
|
||||
// and replaces all occurrences with a given replacement string.
|
||||
func Replace(str, substr, replace cty.Value) (cty.Value, error) {
|
||||
return ReplaceFunc.Call([]cty.Value{str, substr, replace})
|
||||
}
|
||||
|
||||
func RegexReplace(str, substr, replace cty.Value) (cty.Value, error) {
|
||||
return RegexReplaceFunc.Call([]cty.Value{str, substr, replace})
|
||||
}
|
Reference in New Issue
Block a user