2020-05-21 17:23:33 +00:00
|
|
|
package gen
|
|
|
|
|
|
|
|
import (
|
2020-06-06 01:52:00 +00:00
|
|
|
"bytes"
|
2020-05-21 17:23:33 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"math/big"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
2021-06-24 16:17:55 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model"
|
2021-09-30 03:11:56 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/pcl"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
|
2023-06-28 16:02:04 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/slice"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2020-05-21 17:23:33 +00:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
)
|
|
|
|
|
2020-06-30 01:10:34 +00:00
|
|
|
const keywordRange = "range"
|
|
|
|
|
2020-05-21 17:23:33 +00:00
|
|
|
func (g *generator) GetPrecedence(expr model.Expression) int {
|
|
|
|
// TODO: Current values copied from Node, update based on
|
|
|
|
// https://golang.org/ref/spec
|
|
|
|
switch expr := expr.(type) {
|
|
|
|
case *model.ConditionalExpression:
|
|
|
|
return 4
|
|
|
|
case *model.BinaryOpExpression:
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
return 5
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
return 6
|
|
|
|
case hclsyntax.OpEqual, hclsyntax.OpNotEqual:
|
|
|
|
return 11
|
|
|
|
case hclsyntax.OpGreaterThan, hclsyntax.OpGreaterThanOrEqual, hclsyntax.OpLessThan,
|
|
|
|
hclsyntax.OpLessThanOrEqual:
|
|
|
|
return 12
|
|
|
|
case hclsyntax.OpAdd, hclsyntax.OpSubtract:
|
|
|
|
return 14
|
|
|
|
case hclsyntax.OpMultiply, hclsyntax.OpDivide, hclsyntax.OpModulo:
|
|
|
|
return 15
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected binary expression %v", expr)
|
|
|
|
}
|
|
|
|
case *model.UnaryOpExpression:
|
|
|
|
return 17
|
|
|
|
case *model.FunctionCallExpression:
|
|
|
|
switch expr.Name {
|
|
|
|
default:
|
|
|
|
return 20
|
|
|
|
}
|
|
|
|
case *model.ForExpression, *model.IndexExpression, *model.RelativeTraversalExpression, *model.SplatExpression,
|
|
|
|
*model.TemplateJoinExpression:
|
|
|
|
return 20
|
|
|
|
case *model.AnonymousFunctionExpression, *model.LiteralValueExpression, *model.ObjectConsExpression,
|
|
|
|
*model.ScopeTraversalExpression, *model.TemplateExpression, *model.TupleConsExpression:
|
|
|
|
return 22
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected expression %v of type %T", expr, expr)
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenAnonymousFunctionExpression generates code for an AnonymousFunctionExpression.
|
2020-05-26 14:07:54 +00:00
|
|
|
func (g *generator) GenAnonymousFunctionExpression(w io.Writer, expr *model.AnonymousFunctionExpression) {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genAnonymousFunctionExpression(w, expr, nil, false)
|
2020-06-19 00:17:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genAnonymousFunctionExpression(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.AnonymousFunctionExpression,
|
|
|
|
bodyPreamble []string,
|
2021-06-24 16:17:55 +00:00
|
|
|
inApply bool,
|
2020-06-19 00:17:51 +00:00
|
|
|
) {
|
2020-05-26 14:07:54 +00:00
|
|
|
g.Fgenf(w, "func(")
|
|
|
|
leadingSep := ""
|
|
|
|
for _, param := range expr.Signature.Parameters {
|
2020-06-06 01:52:00 +00:00
|
|
|
isInput := isInputty(param.Type)
|
2021-08-19 07:22:27 +00:00
|
|
|
g.Fgenf(w, "%s%s %s", leadingSep, makeValidIdentifier(param.Name), g.argumentTypeName(nil, param.Type, isInput))
|
2020-05-26 14:07:54 +00:00
|
|
|
leadingSep = ", "
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
retType := expr.Signature.ReturnType
|
|
|
|
if inApply {
|
|
|
|
retType = model.ResolveOutputs(retType)
|
|
|
|
}
|
|
|
|
|
|
|
|
retTypeName := g.argumentTypeName(nil, retType, false)
|
|
|
|
g.Fgenf(w, ") (%s, error) {\n", retTypeName)
|
2020-06-16 06:00:02 +00:00
|
|
|
|
2020-06-19 00:17:51 +00:00
|
|
|
for _, decl := range bodyPreamble {
|
|
|
|
g.Fgenf(w, "%s\n", decl)
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
body, temps := g.lowerExpression(expr.Body, retType)
|
|
|
|
g.genTempsMultiReturn(w, temps, retTypeName)
|
2020-06-16 06:00:02 +00:00
|
|
|
|
2022-09-16 23:12:29 +00:00
|
|
|
// g.Fgenf(w, "return %v, nil", body)
|
|
|
|
|
|
|
|
// fromBase64 special case
|
|
|
|
if b, ok := body.(*model.FunctionCallExpression); ok && b.Name == fromBase64Fn {
|
|
|
|
g.Fgenf(w, "value, _ := %v\n", b)
|
|
|
|
g.Fgenf(w, "return pulumi.String(value), nil")
|
|
|
|
} else if strings.HasPrefix(retTypeName, "pulumi") {
|
|
|
|
g.Fgenf(w, "return %s(%v), nil", retTypeName, body)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "return %v, nil", body)
|
|
|
|
}
|
2020-05-26 14:07:54 +00:00
|
|
|
g.Fgenf(w, "\n}")
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
func (g *generator) GenBinaryOpExpression(w io.Writer, expr *model.BinaryOpExpression) {
|
|
|
|
opstr, precedence := "", g.GetPrecedence(expr)
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpAdd:
|
|
|
|
opstr = "+"
|
|
|
|
case hclsyntax.OpDivide:
|
|
|
|
opstr = "/"
|
|
|
|
case hclsyntax.OpEqual:
|
|
|
|
opstr = "=="
|
|
|
|
case hclsyntax.OpGreaterThan:
|
|
|
|
opstr = ">"
|
|
|
|
case hclsyntax.OpGreaterThanOrEqual:
|
|
|
|
opstr = ">="
|
|
|
|
case hclsyntax.OpLessThan:
|
|
|
|
opstr = "<"
|
|
|
|
case hclsyntax.OpLessThanOrEqual:
|
|
|
|
opstr = "<="
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
opstr = "&&"
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
opstr = "||"
|
|
|
|
case hclsyntax.OpModulo:
|
|
|
|
opstr = "%"
|
|
|
|
case hclsyntax.OpMultiply:
|
|
|
|
opstr = "*"
|
|
|
|
case hclsyntax.OpNotEqual:
|
|
|
|
opstr = "!="
|
|
|
|
case hclsyntax.OpSubtract:
|
|
|
|
opstr = "-"
|
|
|
|
default:
|
|
|
|
opstr, precedence = ",", 1
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
g.Fgenf(w, "%.[1]*[2]v %[3]v %.[1]*[4]o", precedence, expr.LeftOperand, opstr, expr.RightOperand)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenConditionalExpression(w io.Writer, expr *model.ConditionalExpression) {
|
|
|
|
// Ternary expressions are not supported in go so we need to allocate temp variables in the parent scope.
|
|
|
|
// This is handled by lower expression and rewriteTernaries
|
|
|
|
contract.Failf("unlowered conditional expression @ %v", expr.SyntaxNode().Range())
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
// GenForExpression generates code for a ForExpression.
|
2022-04-18 09:03:42 +00:00
|
|
|
func (g *generator) GenForExpression(w io.Writer, expr *model.ForExpression) {
|
|
|
|
g.genNYI(w, "For expression")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genSafeEnum(w io.Writer, to *model.EnumType) func(member *schema.Enum) {
|
|
|
|
return func(member *schema.Enum) {
|
|
|
|
// We know the enum value at the call site, so we can directly stamp in a
|
|
|
|
// valid enum instance. We don't need to convert.
|
|
|
|
enumName := tokenToName(to.Token)
|
|
|
|
memberTag := member.Name
|
|
|
|
if memberTag == "" {
|
|
|
|
memberTag = member.Value.(string)
|
|
|
|
}
|
|
|
|
memberTag, err := makeSafeEnumName(memberTag, enumName)
|
|
|
|
contract.AssertNoErrorf(err, "Enum is invalid")
|
2022-11-22 20:44:19 +00:00
|
|
|
pkg, mod, _, _ := pcl.DecomposeToken(to.Token, to.SyntaxNode().Range())
|
|
|
|
mod = g.getModOrAlias(pkg, mod, mod)
|
|
|
|
|
|
|
|
g.Fgenf(w, "%s.%s", mod, memberTag)
|
2022-04-18 09:03:42 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionCallExpression) {
|
|
|
|
switch expr.Name {
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.IntrinsicConvert:
|
2022-04-18 09:03:42 +00:00
|
|
|
from := expr.Args[0]
|
|
|
|
to := pcl.LowerConversion(from, expr.Signature.ReturnType)
|
|
|
|
output, isOutput := to.(*model.OutputType)
|
|
|
|
if isOutput {
|
|
|
|
to = output.ElementType
|
|
|
|
}
|
|
|
|
switch to := to.(type) {
|
|
|
|
case *model.EnumType:
|
|
|
|
var underlyingType string
|
|
|
|
switch {
|
|
|
|
case to.Type.Equals(model.StringType):
|
|
|
|
underlyingType = "string"
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf(
|
|
|
|
"Unsafe enum conversions from type %s not implemented yet: %s => %s",
|
|
|
|
from.Type(), from, to))
|
|
|
|
}
|
2022-09-09 23:11:38 +00:00
|
|
|
pkg, mod, typ, _ := pcl.DecomposeToken(to.Token, to.SyntaxNode().Range())
|
|
|
|
mod = g.getModOrAlias(pkg, mod, mod)
|
|
|
|
enumTag := fmt.Sprintf("%s.%s", mod, typ)
|
2022-04-18 09:03:42 +00:00
|
|
|
if isOutput {
|
|
|
|
g.Fgenf(w,
|
|
|
|
"%.v.ApplyT(func(x *%[3]s) %[2]s { return %[2]s(*x) }).(%[2]sOutput)",
|
|
|
|
from, enumTag, underlyingType)
|
|
|
|
return
|
|
|
|
}
|
2022-12-01 22:41:24 +00:00
|
|
|
diag := pcl.GenEnum(to, from, g.genSafeEnum(w, to), func(from model.Expression) {
|
2022-04-18 09:03:42 +00:00
|
|
|
g.Fgenf(w, "%s(%v)", enumTag, from)
|
|
|
|
})
|
2022-12-01 22:41:24 +00:00
|
|
|
if diag != nil {
|
|
|
|
g.diagnostics = append(g.diagnostics, diag)
|
|
|
|
}
|
2022-04-18 09:03:42 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
switch arg := from.(type) {
|
2020-05-21 17:23:33 +00:00
|
|
|
case *model.TupleConsExpression:
|
|
|
|
g.genTupleConsExpression(w, arg, expr.Type())
|
|
|
|
case *model.ObjectConsExpression:
|
2020-06-18 20:34:22 +00:00
|
|
|
isInput := false
|
|
|
|
g.genObjectConsExpression(w, arg, expr.Type(), isInput)
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.LiteralValueExpression:
|
|
|
|
g.genLiteralValueExpression(w, arg, expr.Type())
|
2021-06-24 16:17:55 +00:00
|
|
|
case *model.TemplateExpression:
|
|
|
|
g.genTemplateExpression(w, arg, expr.Type())
|
|
|
|
case *model.ScopeTraversalExpression:
|
|
|
|
g.genScopeTraversalExpression(w, arg, expr.Type())
|
2020-05-21 17:23:33 +00:00
|
|
|
default:
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%.v", expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.IntrinsicApply:
|
2020-05-26 14:07:54 +00:00
|
|
|
g.genApply(w, expr)
|
2020-05-21 17:23:33 +00:00
|
|
|
case "element":
|
|
|
|
g.genNYI(w, "element")
|
|
|
|
case "entries":
|
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// switch model.ResolveOutputs(expr.Args[0].Type()).(type) {
|
|
|
|
// case *model.ListType, *model.TupleType:
|
|
|
|
// if call, ok := expr.Args[0].(*model.FunctionCallExpression); ok && call.Name == "range" {
|
|
|
|
// g.genRange(w, call, true)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// g.Fgenf(w, "%.20v.Select((v, k)", expr.Args[0])
|
|
|
|
// case *model.MapType, *model.ObjectType:
|
|
|
|
// g.genNYI(w, "MapOrObjectEntries")
|
|
|
|
// }
|
|
|
|
// g.Fgenf(w, " => new { Key = k, Value = v })")
|
|
|
|
case "fileArchive":
|
2022-01-21 14:03:25 +00:00
|
|
|
g.Fgenf(w, "pulumi.NewFileArchive(%.v)", expr.Args[0])
|
2022-04-25 19:59:30 +00:00
|
|
|
case "remoteArchive":
|
|
|
|
g.Fgenf(w, "pulumi.NewRemoteArchive(%.v)", expr.Args[0])
|
|
|
|
case "assetArchive":
|
|
|
|
g.Fgenf(w, "pulumi.NewAssetArchive(%.v)", expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "fileAsset":
|
2020-06-16 06:00:02 +00:00
|
|
|
g.Fgenf(w, "pulumi.NewFileAsset(%.v)", expr.Args[0])
|
2022-04-25 19:59:30 +00:00
|
|
|
case "stringAsset":
|
|
|
|
g.Fgenf(w, "pulumi.NewStringAsset(%.v)", expr.Args[0])
|
|
|
|
case "remoteAsset":
|
|
|
|
g.Fgenf(w, "pulumi.NewRemoteAsset(%.v)", expr.Args[0])
|
2021-09-10 22:09:28 +00:00
|
|
|
case "filebase64":
|
|
|
|
// Assuming the existence of the following helper method
|
|
|
|
g.Fgenf(w, "filebase64OrPanic(%v)", expr.Args[0])
|
2022-01-21 14:03:25 +00:00
|
|
|
case "filebase64sha256":
|
|
|
|
// Assuming the existence of the following helper method
|
|
|
|
g.Fgenf(w, "filebase64sha256OrPanic(%v)", expr.Args[0])
|
2023-03-10 11:14:28 +00:00
|
|
|
case "notImplemented":
|
|
|
|
g.Fgenf(w, "notImplemented(%v)", expr.Args[0])
|
2023-06-11 09:05:10 +00:00
|
|
|
case "singleOrNone":
|
|
|
|
g.Fgenf(w, "singleOrNone(%v)", expr.Args[0])
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.Invoke:
|
2023-01-11 22:17:14 +00:00
|
|
|
if expr.Signature.MultiArgumentInputs {
|
|
|
|
panic(fmt.Errorf("go program-gen does not implement MultiArgumentInputs for function '%v'",
|
|
|
|
expr.Args[0]))
|
|
|
|
}
|
2022-08-12 18:04:21 +00:00
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
pkg, module, fn, diags := g.functionName(expr.Args[0])
|
2022-08-12 18:04:21 +00:00
|
|
|
contract.Assertf(len(diags) == 0, "We don't allow problems getting the function name")
|
2023-07-10 13:05:18 +00:00
|
|
|
if module == "" || module == "index" {
|
2020-06-18 00:19:39 +00:00
|
|
|
module = pkg
|
|
|
|
}
|
2021-11-18 22:50:51 +00:00
|
|
|
isOut, outArgs, outArgsType := pcl.RecognizeOutputVersionedInvoke(expr)
|
|
|
|
if isOut {
|
2022-08-12 18:04:21 +00:00
|
|
|
outTypeName, err := outputVersionFunctionArgTypeName(outArgsType, g.externalCache)
|
2021-11-18 22:50:51 +00:00
|
|
|
if err != nil {
|
2022-08-12 18:04:21 +00:00
|
|
|
// We create a diag instead of panicking since panics are caught in go
|
|
|
|
// format expressions.
|
|
|
|
g.diagnostics = append(g.diagnostics, &hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Error when generating an output-versioned Invoke",
|
|
|
|
Detail: fmt.Sprintf("underlying error: %v", err),
|
|
|
|
Subject: &hcl.Range{},
|
|
|
|
Context: &hcl.Range{},
|
|
|
|
Expression: nil,
|
|
|
|
EvalContext: &hcl.EvalContext{},
|
|
|
|
})
|
|
|
|
g.Fgenf(w, "%q", "failed") // Write a value to avoid syntax errors
|
|
|
|
return
|
2021-11-18 22:50:51 +00:00
|
|
|
}
|
|
|
|
g.Fgenf(w, "%s.%sOutput(ctx, ", module, fn)
|
|
|
|
g.genObjectConsExpressionWithTypeName(w, outArgs, outArgsType, outTypeName)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%s.%s(ctx, ", module, fn)
|
|
|
|
g.Fgenf(w, "%.v", expr.Args[1])
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
|
|
|
optionsBag := ""
|
|
|
|
var buf bytes.Buffer
|
|
|
|
if len(expr.Args) == 3 {
|
|
|
|
g.Fgenf(&buf, ", %.v", expr.Args[2])
|
|
|
|
} else {
|
|
|
|
g.Fgenf(&buf, ", nil")
|
|
|
|
}
|
|
|
|
optionsBag = buf.String()
|
|
|
|
g.Fgenf(w, "%v)", optionsBag)
|
2021-07-29 03:41:23 +00:00
|
|
|
case "join":
|
|
|
|
g.Fgenf(w, "strings.Join(%v, %v)", expr.Args[1], expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "length":
|
2023-05-08 14:20:04 +00:00
|
|
|
g.Fgenf(w, "len(%.20v)", expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "lookup":
|
|
|
|
g.genNYI(w, "Lookup")
|
2020-06-30 01:10:34 +00:00
|
|
|
case keywordRange:
|
2020-05-21 17:23:33 +00:00
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// g.genRange(w, expr, false)
|
|
|
|
case "readFile":
|
2021-09-09 19:10:25 +00:00
|
|
|
// Assuming the existence of the following helper method located earlier in the preamble
|
|
|
|
g.Fgenf(w, "readFileOrPanic(%v)", expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "readDir":
|
2021-06-24 16:17:55 +00:00
|
|
|
contract.Failf("unlowered readDir function expression @ %v", expr.SyntaxNode().Range())
|
2020-06-30 18:35:24 +00:00
|
|
|
case "secret":
|
|
|
|
outputTypeName := "pulumi.Any"
|
|
|
|
if model.ResolveOutputs(expr.Type()) != model.DynamicType {
|
|
|
|
outputTypeName = g.argumentTypeName(nil, expr.Type(), false)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "pulumi.ToSecret(%v).(%sOutput)", expr.Args[0], outputTypeName)
|
2023-01-31 12:31:00 +00:00
|
|
|
case "unsecret":
|
|
|
|
outputTypeName := "pulumi.Any"
|
|
|
|
if model.ResolveOutputs(expr.Type()) != model.DynamicType {
|
|
|
|
outputTypeName = g.argumentTypeName(nil, expr.Type(), false)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "pulumi.Unsecret(%v).(%sOutput)", expr.Args[0], outputTypeName)
|
2020-05-21 17:23:33 +00:00
|
|
|
case "split":
|
2020-06-18 00:19:39 +00:00
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// g.Fgenf(w, "%.20v.Split(%v)", expr.Args[1], expr.Args[0])
|
2021-07-29 03:41:23 +00:00
|
|
|
case "toBase64":
|
|
|
|
g.Fgenf(w, "base64.StdEncoding.EncodeToString([]byte(%v))", expr.Args[0])
|
2022-09-16 23:12:29 +00:00
|
|
|
case fromBase64Fn:
|
|
|
|
g.Fgenf(w, "base64.StdEncoding.DecodeString(%v)", expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "toJSON":
|
2020-06-06 01:52:00 +00:00
|
|
|
contract.Failf("unlowered toJSON function expression @ %v", expr.SyntaxNode().Range())
|
2020-06-16 06:00:02 +00:00
|
|
|
case "mimeType":
|
|
|
|
g.Fgenf(w, "mime.TypeByExtension(path.Ext(%.v))", expr.Args[0])
|
2021-09-10 18:40:38 +00:00
|
|
|
case "sha1":
|
|
|
|
g.Fgenf(w, "sha1Hash(%v)", expr.Args[0])
|
[codegen/go] Improve optional params in invoke
As described in #8821, docs generated for helper functions can be incorrect because optional arguments to parameter objects are not correctly handled.
Previously, code would be generated like so:
```go
policyDocument, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
iam.GetPolicyDocumentStatement{
Sid: "1",
//...
```
However "Sid" is of type `*string`.
This four helper conversion functions, to handle the primitive types we lower from, and modifies codegen to recursively apply these functions inside of an invoke call.
In the new code generation, the above is instead rendered as:
```go
policyDocument, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
iam.GetPolicyDocumentStatement{
Sid: pulumi.StringRef("1"),
//...
```
2022-02-01 03:18:15 +00:00
|
|
|
case "goOptionalFloat64":
|
|
|
|
g.Fgenf(w, "pulumi.Float64Ref(%.v)", expr.Args[0])
|
|
|
|
case "goOptionalBool":
|
|
|
|
g.Fgenf(w, "pulumi.BoolRef(%.v)", expr.Args[0])
|
|
|
|
case "goOptionalInt":
|
|
|
|
g.Fgenf(w, "pulumi.IntRef(%.v)", expr.Args[0])
|
|
|
|
case "goOptionalString":
|
|
|
|
g.Fgenf(w, "pulumi.StringRef(%.v)", expr.Args[0])
|
2022-03-16 00:05:36 +00:00
|
|
|
case "stack":
|
|
|
|
g.Fgen(w, "ctx.Stack()")
|
|
|
|
case "project":
|
|
|
|
g.Fgen(w, "ctx.Project()")
|
|
|
|
case "cwd":
|
|
|
|
g.Fgen(w, "func(cwd string, err error) string { if err != nil { panic(err) }; return cwd }(os.Getwd())")
|
2020-05-21 17:23:33 +00:00
|
|
|
default:
|
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-18 22:50:51 +00:00
|
|
|
// Currently args type for output-versioned invokes are named
|
|
|
|
// `FOutputArgs`, but this is not yet understood by `tokenToType`. Use
|
|
|
|
// this function to compensate.
|
2022-08-12 18:04:21 +00:00
|
|
|
func outputVersionFunctionArgTypeName(t model.Type, cache *Cache) (string, error) {
|
2021-11-18 22:50:51 +00:00
|
|
|
schemaType, ok := pcl.GetSchemaForType(t)
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("No schema.Type type found for the given model.Type")
|
|
|
|
}
|
|
|
|
|
|
|
|
objType, ok := schemaType.(*schema.ObjectType)
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("Expected a schema.ObjectType, got %s", schemaType.String())
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
pkg := &pkgContext{
|
|
|
|
pkg: (&schema.Package{Name: "main"}).Reference(),
|
|
|
|
externalPackages: cache,
|
|
|
|
}
|
2021-11-18 22:50:51 +00:00
|
|
|
|
|
|
|
var ty string
|
|
|
|
if pkg.isExternalReference(objType) {
|
2022-07-06 18:35:31 +00:00
|
|
|
extPkg, _ := pkg.contextForExternalReference(objType)
|
|
|
|
ty = extPkg.tokenToType(objType.Token)
|
2021-11-18 22:50:51 +00:00
|
|
|
} else {
|
|
|
|
ty = pkg.tokenToType(objType.Token)
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%sOutputArgs", strings.TrimSuffix(ty, "Args")), nil
|
|
|
|
}
|
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
func (g *generator) GenIndexExpression(w io.Writer, expr *model.IndexExpression) {
|
|
|
|
g.Fgenf(w, "%.20v[%.v]", expr.Collection, expr.Key)
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
func (g *generator) GenLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression) {
|
2020-06-06 01:52:00 +00:00
|
|
|
g.genLiteralValueExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression, destType model.Type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
exprType := expr.Type()
|
|
|
|
if cns, ok := exprType.(*model.ConstType); ok {
|
|
|
|
exprType = cns.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
if exprType == model.NoneType {
|
2020-08-07 02:09:18 +00:00
|
|
|
g.Fgen(w, "nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
argTypeName := g.argumentTypeName(expr, destType, false)
|
2020-06-06 01:52:00 +00:00
|
|
|
isPulumiType := strings.HasPrefix(argTypeName, "pulumi.")
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
switch exprType {
|
|
|
|
case model.BoolType:
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(%v)", argTypeName, expr.Value.True())
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%v", expr.Value.True())
|
|
|
|
}
|
|
|
|
case model.NumberType, model.IntType:
|
|
|
|
bf := expr.Value.AsBigFloat()
|
|
|
|
if i, acc := bf.Int64(); acc == big.Exact {
|
2020-06-06 01:52:00 +00:00
|
|
|
if isPulumiType {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s(%d)", argTypeName, i)
|
2020-06-06 01:52:00 +00:00
|
|
|
} else {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%d", i)
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
} else {
|
|
|
|
f, _ := bf.Float64()
|
2020-06-06 01:52:00 +00:00
|
|
|
if isPulumiType {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s(%g)", argTypeName, f)
|
2020-06-06 01:52:00 +00:00
|
|
|
} else {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%g", f)
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
case model.StringType:
|
|
|
|
strVal := expr.Value.AsString()
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(", argTypeName)
|
2023-06-22 17:43:06 +00:00
|
|
|
g.genStringLiteral(w, strVal, true /* allow raw */)
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, ")")
|
|
|
|
} else {
|
2023-06-22 17:43:06 +00:00
|
|
|
g.genStringLiteral(w, strVal, true /* allow raw */)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
default:
|
2021-06-24 16:17:55 +00:00
|
|
|
contract.Failf("unexpected opaque type in GenLiteralValueExpression: %v (%v)", destType,
|
2020-05-21 17:23:33 +00:00
|
|
|
expr.SyntaxNode().Range())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression) {
|
2023-05-08 14:20:04 +00:00
|
|
|
switch argType := expr.Type().(type) {
|
|
|
|
case *model.ObjectType:
|
|
|
|
if len(argType.Annotations) > 0 {
|
|
|
|
if configMetadata, ok := argType.Annotations[0].(*ObjectTypeFromConfigMetadata); ok {
|
|
|
|
g.genObjectConsExpressionWithTypeName(w, expr, expr.Type(), configMetadata.TypeName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
isInput := false
|
|
|
|
g.genObjectConsExpression(w, expr, expr.Type(), isInput)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
func (g *generator) genObjectConsExpression(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.ObjectConsExpression,
|
|
|
|
destType model.Type,
|
2023-03-03 16:36:39 +00:00
|
|
|
isInput bool,
|
|
|
|
) {
|
2021-06-24 16:17:55 +00:00
|
|
|
isInput = isInput || isInputty(destType)
|
2021-11-18 22:50:51 +00:00
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
typeName := g.argumentTypeName(expr, destType, isInput)
|
2021-09-30 03:11:56 +00:00
|
|
|
if schemaType, ok := pcl.GetSchemaForType(destType); ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
if obj, ok := codegen.UnwrapType(schemaType).(*schema.ObjectType); ok {
|
|
|
|
if g.useLookupInvokeForm(obj.Token) {
|
|
|
|
typeName = strings.Replace(typeName, ".Get", ".Lookup", 1)
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2021-11-18 22:50:51 +00:00
|
|
|
g.genObjectConsExpressionWithTypeName(w, expr, destType, typeName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genObjectConsExpressionWithTypeName(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.ObjectConsExpression,
|
|
|
|
destType model.Type,
|
2023-03-03 16:36:39 +00:00
|
|
|
typeName string,
|
|
|
|
) {
|
2021-11-18 22:50:51 +00:00
|
|
|
if len(expr.Items) == 0 {
|
|
|
|
g.Fgenf(w, "nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var temps []interface{}
|
2021-06-24 16:17:55 +00:00
|
|
|
// TODO: @pgavlin --- ineffectual assignment, was there some work in flight here?
|
|
|
|
// if strings.HasSuffix(typeName, "Args") {
|
|
|
|
// isInput = true
|
|
|
|
// }
|
|
|
|
// // invokes are not inputty
|
|
|
|
// if strings.Contains(typeName, ".Lookup") || strings.Contains(typeName, ".Get") {
|
|
|
|
// isInput = false
|
|
|
|
// }
|
|
|
|
isMap := strings.HasPrefix(typeName, "map[")
|
|
|
|
|
|
|
|
// TODO: retrieve schema and propagate optionals to emit bool ptr, etc.
|
|
|
|
|
|
|
|
// first lower all inner expressions and emit temps
|
|
|
|
for i, item := range expr.Items {
|
|
|
|
// don't treat keys as inputs
|
2023-01-06 00:07:45 +00:00
|
|
|
//nolint:revive
|
2021-06-24 16:17:55 +00:00
|
|
|
k, kTemps := g.lowerExpression(item.Key, item.Key.Type())
|
|
|
|
temps = append(temps, kTemps...)
|
|
|
|
item.Key = k
|
|
|
|
x, xTemps := g.lowerExpression(item.Value, item.Value.Type())
|
2023-11-03 14:22:43 +00:00
|
|
|
x, invokeTemps := g.rewriteInlineInvokes(x)
|
2021-06-24 16:17:55 +00:00
|
|
|
temps = append(temps, xTemps...)
|
2023-11-03 14:22:43 +00:00
|
|
|
for _, t := range invokeTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
item.Value = x
|
|
|
|
expr.Items[i] = item
|
|
|
|
}
|
|
|
|
g.genTemps(w, temps)
|
|
|
|
|
2022-12-15 22:45:07 +00:00
|
|
|
if g.inGenTupleConExprListArgs {
|
|
|
|
if g.isPtrArg {
|
|
|
|
g.Fgenf(w, "&%s", typeName)
|
|
|
|
}
|
|
|
|
} else if isMap || !strings.HasSuffix(typeName, "Args") || strings.HasSuffix(typeName, "OutputArgs") {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s", typeName)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "&%s", typeName)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "{\n")
|
|
|
|
|
|
|
|
for _, item := range expr.Items {
|
|
|
|
if lit, ok := g.literalKey(item.Key); ok {
|
|
|
|
if isMap || strings.HasSuffix(typeName, "Map") {
|
|
|
|
g.Fgenf(w, "\"%s\"", lit)
|
2020-06-06 01:52:00 +00:00
|
|
|
} else {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s", Title(lit))
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%.v", item.Key)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, ": %.v,\n", item.Value)
|
2020-06-18 00:19:39 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
|
|
|
|
g.Fgenf(w, "}")
|
2020-06-18 00:19:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 17:23:33 +00:00
|
|
|
func (g *generator) GenRelativeTraversalExpression(w io.Writer, expr *model.RelativeTraversalExpression) {
|
|
|
|
g.Fgenf(w, "%.20v", expr.Source)
|
2020-06-18 00:19:39 +00:00
|
|
|
isRootResource := false
|
|
|
|
if ie, ok := expr.Source.(*model.IndexExpression); ok {
|
|
|
|
if se, ok := ie.Collection.(*model.ScopeTraversalExpression); ok {
|
2021-09-30 03:11:56 +00:00
|
|
|
if _, ok := se.Parts[0].(*pcl.Resource); ok {
|
2020-06-18 00:19:39 +00:00
|
|
|
isRootResource = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal, expr.Parts, isRootResource)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenScopeTraversalExpression(w io.Writer, expr *model.ScopeTraversalExpression) {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genScopeTraversalExpression(w, expr, expr.Type())
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
func (g *generator) genScopeTraversalExpression(
|
2023-03-03 16:36:39 +00:00
|
|
|
w io.Writer, expr *model.ScopeTraversalExpression, destType model.Type,
|
|
|
|
) {
|
2020-05-21 17:23:33 +00:00
|
|
|
rootName := expr.RootName
|
2020-06-18 00:19:39 +00:00
|
|
|
|
|
|
|
if _, ok := expr.Parts[0].(*model.SplatVariable); ok {
|
|
|
|
rootName = "val0"
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
2020-06-06 01:52:00 +00:00
|
|
|
genIDCall := false
|
2020-05-21 17:23:33 +00:00
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
isInput := false
|
2021-09-30 03:11:56 +00:00
|
|
|
if schemaType, ok := pcl.GetSchemaForType(destType); ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
_, isInput = schemaType.(*schema.InputType)
|
|
|
|
}
|
|
|
|
|
2022-11-29 00:14:07 +00:00
|
|
|
var sourceIsPlain bool
|
|
|
|
switch root := expr.Parts[0].(type) {
|
|
|
|
case *pcl.Resource:
|
2020-06-06 01:52:00 +00:00
|
|
|
isInput = false
|
2022-11-29 00:14:07 +00:00
|
|
|
if _, ok := pcl.GetSchemaForType(root.InputType); ok {
|
2020-06-06 01:52:00 +00:00
|
|
|
// convert .id into .ID()
|
|
|
|
last := expr.Traversal[len(expr.Traversal)-1]
|
|
|
|
if attr, ok := last.(hcl.TraverseAttr); ok && attr.Name == "id" {
|
|
|
|
genIDCall = true
|
|
|
|
expr.Traversal = expr.Traversal[:len(expr.Traversal)-1]
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2022-11-29 00:14:07 +00:00
|
|
|
case *pcl.LocalVariable:
|
|
|
|
if root, ok := root.Definition.Value.(*model.FunctionCallExpression); ok && !pcl.IsOutputVersionInvokeCall(root) {
|
|
|
|
sourceIsPlain = true
|
|
|
|
}
|
2023-05-08 14:20:04 +00:00
|
|
|
case *pcl.ConfigVariable:
|
|
|
|
if g.isComponent {
|
|
|
|
// config variables of components are always of type Input<T>
|
|
|
|
// these shouldn't be wrapped in a pulumi.String(...), pulumi.Int(...) etc. functions
|
|
|
|
g.Fgenf(w, "args.%s", Title(rootName))
|
|
|
|
isRootResource := false
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts[1:], isRootResource)
|
|
|
|
return
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
|
|
|
// TODO if it's an array type, we need a lowering step to turn []string -> pulumi.StringArray
|
|
|
|
if isInput {
|
2022-11-29 00:14:07 +00:00
|
|
|
argTypeName := g.argumentTypeName(expr, expr.Type(), isInput)
|
|
|
|
if strings.HasSuffix(argTypeName, "Array") {
|
2021-06-24 16:17:55 +00:00
|
|
|
destTypeName := g.argumentTypeName(expr, destType, isInput)
|
2022-11-29 00:14:07 +00:00
|
|
|
// `argTypeName` == `destTypeName` and `argTypeName` ends with `Array`, we
|
|
|
|
// know that `destType` is an outputty type. If the source is plain (and thus
|
|
|
|
// not outputty), then the types can never line up and we will need a
|
|
|
|
// conversion helper method.
|
|
|
|
if argTypeName != destTypeName || sourceIsPlain {
|
2021-06-24 16:17:55 +00:00
|
|
|
// use a helper to transform prompt arrays into inputty arrays
|
|
|
|
var helper *promptToInputArrayHelper
|
2022-11-29 00:14:07 +00:00
|
|
|
if h, ok := g.arrayHelpers[argTypeName]; ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
helper = h
|
|
|
|
} else {
|
|
|
|
// helpers are emitted at the end in the postamble step
|
|
|
|
helper = &promptToInputArrayHelper{
|
2022-11-29 00:14:07 +00:00
|
|
|
destType: argTypeName,
|
2021-06-24 16:17:55 +00:00
|
|
|
}
|
2022-11-29 00:14:07 +00:00
|
|
|
g.arrayHelpers[argTypeName] = helper
|
2020-06-24 18:07:26 +00:00
|
|
|
}
|
2022-11-29 00:14:07 +00:00
|
|
|
// Wrap the emitted expression in a call to the generated helper function.
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s(", helper.getFnName())
|
|
|
|
defer g.Fgenf(w, ")")
|
2020-06-24 18:07:26 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-11-29 00:14:07 +00:00
|
|
|
// Wrap the emitted expression in a type conversion.
|
2020-06-24 18:07:26 +00:00
|
|
|
g.Fgenf(w, "%s(", g.argumentTypeName(expr, expr.Type(), isInput))
|
2021-06-24 16:17:55 +00:00
|
|
|
defer g.Fgenf(w, ")")
|
2020-06-24 18:07:26 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
// TODO: this isn't exhaustively correct as "range" could be a legit var name
|
|
|
|
// instead we should probably use a fn call expression here for entries/range
|
|
|
|
// similar to other languages
|
2020-06-30 01:10:34 +00:00
|
|
|
if rootName == keywordRange {
|
2020-06-16 06:00:02 +00:00
|
|
|
part := expr.Traversal[1].(hcl.TraverseAttr).Name
|
|
|
|
switch part {
|
|
|
|
case "value":
|
|
|
|
g.Fgenf(w, "val0")
|
|
|
|
case "key":
|
|
|
|
g.Fgenf(w, "key0")
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal on range expression: %s", part)
|
|
|
|
}
|
|
|
|
} else {
|
2020-06-30 01:10:34 +00:00
|
|
|
g.Fgen(w, makeValidIdentifier(rootName))
|
2020-06-18 00:19:39 +00:00
|
|
|
isRootResource := false
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts[1:], isRootResource)
|
2020-06-16 06:00:02 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
|
|
|
if genIDCall {
|
|
|
|
g.Fgenf(w, ".ID()")
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GenSplatExpression generates code for a SplatExpression.
|
2020-06-18 00:19:39 +00:00
|
|
|
func (g *generator) GenSplatExpression(w io.Writer, expr *model.SplatExpression) {
|
|
|
|
contract.Failf("unlowered splat expression @ %v", expr.SyntaxNode().Range())
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
// GenTemplateExpression generates code for a TemplateExpression.
|
2020-05-27 14:32:13 +00:00
|
|
|
func (g *generator) GenTemplateExpression(w io.Writer, expr *model.TemplateExpression) {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genTemplateExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genTemplateExpression(w io.Writer, expr *model.TemplateExpression, destType model.Type) {
|
2020-05-27 14:32:13 +00:00
|
|
|
if len(expr.Parts) == 1 {
|
2021-06-24 16:17:55 +00:00
|
|
|
if lit, ok := expr.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) {
|
|
|
|
g.genLiteralValueExpression(w, lit, destType)
|
|
|
|
}
|
|
|
|
|
2022-06-02 16:36:33 +00:00
|
|
|
// If we have a template expression that doesn't start with a string, it indicates
|
|
|
|
// an invalid *pcl.Program. Instead of crashing, we continue.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
argTypeName := g.argumentTypeName(expr, destType, false)
|
|
|
|
isPulumiType := strings.HasPrefix(argTypeName, "pulumi.")
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(", argTypeName)
|
|
|
|
defer g.Fgenf(w, ")")
|
|
|
|
}
|
|
|
|
|
|
|
|
var fmtStr strings.Builder
|
|
|
|
args := new(bytes.Buffer)
|
|
|
|
canBeRaw := true
|
|
|
|
for _, v := range expr.Parts {
|
|
|
|
if lit, ok := v.(*model.LiteralValueExpression); ok && lit.Value.Type().Equals(cty.String) {
|
|
|
|
str := lit.Value.AsString()
|
|
|
|
// We don't want to accidentally embed a formatting directive in our
|
|
|
|
// formatting string.
|
|
|
|
if !strings.ContainsRune(str, '%') {
|
|
|
|
if canBeRaw && strings.ContainsRune(str, '`') {
|
|
|
|
canBeRaw = false
|
|
|
|
}
|
|
|
|
// Build the formatting string
|
|
|
|
fmtStr.WriteString(str)
|
|
|
|
continue
|
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
}
|
2022-06-02 16:36:33 +00:00
|
|
|
// v cannot be directly inserted into the formatting string, so put it in the
|
|
|
|
// argument list.
|
|
|
|
fmtStr.WriteString("%v")
|
|
|
|
g.Fgenf(args, ", %.v", v)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "fmt.Sprintf(")
|
2023-06-22 17:43:06 +00:00
|
|
|
g.genStringLiteral(w, fmtStr.String(), canBeRaw)
|
2022-06-02 16:36:33 +00:00
|
|
|
_, err := args.WriteTo(w)
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.AssertNoErrorf(err, "Failed to write arguments")
|
2022-06-02 16:36:33 +00:00
|
|
|
g.Fgenf(w, ")")
|
2020-05-27 14:32:13 +00:00
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
// GenTemplateJoinExpression generates code for a TemplateJoinExpression.
|
|
|
|
func (g *generator) GenTemplateJoinExpression(w io.Writer, expr *model.TemplateJoinExpression) { /*TODO*/
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenTupleConsExpression(w io.Writer, expr *model.TupleConsExpression) {
|
|
|
|
g.genTupleConsExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenTupleConsExpression generates code for a TupleConsExpression.
|
|
|
|
func (g *generator) genTupleConsExpression(w io.Writer, expr *model.TupleConsExpression, destType model.Type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
isInput := isInputty(destType)
|
2020-06-06 01:52:00 +00:00
|
|
|
|
|
|
|
var temps []interface{}
|
|
|
|
for i, item := range expr.Expressions {
|
2021-06-24 16:17:55 +00:00
|
|
|
item, itemTemps := g.lowerExpression(item, item.Type())
|
2023-11-03 14:22:43 +00:00
|
|
|
item, invokeTemps := g.rewriteInlineInvokes(item)
|
2020-06-06 01:52:00 +00:00
|
|
|
temps = append(temps, itemTemps...)
|
2023-11-03 14:22:43 +00:00
|
|
|
for _, t := range invokeTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
expr.Expressions[i] = item
|
|
|
|
}
|
|
|
|
g.genTemps(w, temps)
|
2020-06-18 20:34:22 +00:00
|
|
|
argType := g.argumentTypeName(expr, destType, isInput)
|
2022-12-15 22:45:07 +00:00
|
|
|
// don't need to generate type for list args if not a pointer, i.e. []ec2.SubnetSpecArgs{ {Type: ...} }
|
|
|
|
// unless it contains an interface, i.e. []map[string]interface{ map[string]interface{"key": "val"} }
|
|
|
|
if strings.HasPrefix(argType, "[]") && !strings.Contains(argType, "interface{}") {
|
|
|
|
defer func(b bool) { g.inGenTupleConExprListArgs = b }(g.inGenTupleConExprListArgs)
|
|
|
|
g.inGenTupleConExprListArgs = true
|
|
|
|
if strings.HasPrefix(argType, "[]*") {
|
|
|
|
defer func(b bool) { g.isPtrArg = b }(g.isPtrArg)
|
|
|
|
g.isPtrArg = true
|
|
|
|
}
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
g.Fgenf(w, "%s{\n", argType)
|
|
|
|
switch len(expr.Expressions) {
|
|
|
|
case 0:
|
|
|
|
// empty array
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
for _, v := range expr.Expressions {
|
|
|
|
g.Fgenf(w, "%v,\n", v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "}")
|
|
|
|
}
|
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
func (g *generator) GenUnaryOpExpression(w io.Writer, expr *model.UnaryOpExpression) {
|
|
|
|
opstr, precedence := "", g.GetPrecedence(expr)
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalNot:
|
|
|
|
opstr = "!"
|
|
|
|
case hclsyntax.OpNegate:
|
|
|
|
opstr = "-"
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "%[2]v%.[1]*[3]v", precedence, opstr, expr.Operand)
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
// argumentTypeName computes the go type for the given expression and model type.
|
2021-06-24 16:17:55 +00:00
|
|
|
func (g *generator) argumentTypeName(expr model.Expression, destType model.Type, isInput bool) (result string) {
|
|
|
|
if cns, ok := destType.(*model.ConstType); ok {
|
|
|
|
destType = cns.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
// This can happen with null literals.
|
|
|
|
if destType == model.NoneType {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-09-30 03:11:56 +00:00
|
|
|
if schemaType, ok := pcl.GetSchemaForType(destType); ok {
|
2022-12-08 10:45:46 +00:00
|
|
|
return (&pkgContext{
|
|
|
|
pkg: (&schema.Package{Name: "main"}).Reference(),
|
|
|
|
externalPackages: g.externalCache,
|
|
|
|
}).argsType(schemaType)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
switch destType := destType.(type) {
|
2020-05-26 14:07:54 +00:00
|
|
|
case *model.OpaqueType:
|
2022-06-13 18:13:03 +00:00
|
|
|
switch *destType {
|
|
|
|
case *model.IntType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Int"
|
|
|
|
}
|
|
|
|
return "int"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.NumberType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Float64"
|
|
|
|
}
|
2020-05-28 20:47:49 +00:00
|
|
|
return "float64"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.StringType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.String"
|
|
|
|
}
|
|
|
|
return "string"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.BoolType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Bool"
|
|
|
|
}
|
|
|
|
return "bool"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.DynamicType:
|
2020-06-30 18:35:24 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Any"
|
|
|
|
}
|
|
|
|
return "interface{}"
|
2020-05-28 20:47:49 +00:00
|
|
|
default:
|
2022-06-13 18:13:03 +00:00
|
|
|
return string(*destType)
|
2020-05-28 20:47:49 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.ObjectType:
|
2020-06-29 22:29:41 +00:00
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
if isInput {
|
2020-06-29 22:29:41 +00:00
|
|
|
// check for element type uniformity and return appropriate type if so
|
|
|
|
allSameType := true
|
|
|
|
var elmType string
|
|
|
|
for _, v := range destType.Properties {
|
|
|
|
valType := g.argumentTypeName(nil, v, true)
|
|
|
|
if elmType != "" && elmType != valType {
|
|
|
|
allSameType = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
elmType = valType
|
|
|
|
}
|
|
|
|
if allSameType && elmType != "" {
|
|
|
|
return fmt.Sprintf("%sMap", elmType)
|
|
|
|
}
|
2020-06-18 00:19:39 +00:00
|
|
|
return "pulumi.Map"
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
return "map[string]interface{}"
|
|
|
|
case *model.MapType:
|
2020-06-18 20:34:22 +00:00
|
|
|
valType := g.argumentTypeName(nil, destType.ElementType, isInput)
|
2020-06-18 00:19:39 +00:00
|
|
|
if isInput {
|
2023-10-20 16:12:25 +00:00
|
|
|
if Title(valType) == "pulumi.Any" {
|
|
|
|
return "pulumi.Map"
|
|
|
|
}
|
2020-06-18 00:19:39 +00:00
|
|
|
return fmt.Sprintf("pulumi.%sMap", Title(valType))
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
return fmt.Sprintf("map[string]%s", valType)
|
|
|
|
case *model.ListType:
|
2020-06-18 20:34:22 +00:00
|
|
|
argTypeName := g.argumentTypeName(nil, destType.ElementType, isInput)
|
2020-06-29 23:33:52 +00:00
|
|
|
if strings.HasPrefix(argTypeName, "pulumi.") && argTypeName != "pulumi.Resource" {
|
2023-10-20 16:12:25 +00:00
|
|
|
if argTypeName == "pulumi.Any" {
|
|
|
|
return "pulumi.Array"
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
return fmt.Sprintf("%sArray", argTypeName)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("[]%s", argTypeName)
|
|
|
|
case *model.TupleType:
|
|
|
|
// attempt to collapse tuple types. intentionally does not use model.UnifyTypes
|
|
|
|
// correct go code requires all types to match, or use of interface{}
|
|
|
|
var elmType model.Type
|
|
|
|
for i, t := range destType.ElementTypes {
|
|
|
|
if i == 0 {
|
|
|
|
elmType = t
|
2021-06-24 16:17:55 +00:00
|
|
|
if cns, ok := elmType.(*model.ConstType); ok {
|
|
|
|
elmType = cns.Type
|
|
|
|
}
|
|
|
|
continue
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
if !elmType.AssignableFrom(t) {
|
2020-06-06 01:52:00 +00:00
|
|
|
elmType = nil
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if elmType != nil {
|
2020-06-18 20:34:22 +00:00
|
|
|
argTypeName := g.argumentTypeName(nil, elmType, isInput)
|
2020-06-29 23:33:52 +00:00
|
|
|
if strings.HasPrefix(argTypeName, "pulumi.") && argTypeName != "pulumi.Resource" {
|
2023-10-20 16:12:25 +00:00
|
|
|
if argTypeName == "pulumi.Any" {
|
|
|
|
return "pulumi.Array"
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
return fmt.Sprintf("%sArray", argTypeName)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("[]%s", argTypeName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if isInput {
|
|
|
|
return "pulumi.Array"
|
|
|
|
}
|
|
|
|
return "[]interface{}"
|
|
|
|
case *model.OutputType:
|
|
|
|
isInput = true
|
2020-06-18 20:34:22 +00:00
|
|
|
return g.argumentTypeName(expr, destType.ElementType, isInput)
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.UnionType:
|
|
|
|
for _, ut := range destType.ElementTypes {
|
2022-12-12 23:18:35 +00:00
|
|
|
isOptional := false
|
|
|
|
// check if the union contains none, which indicates this is an optional value
|
|
|
|
for _, ut := range destType.ElementTypes {
|
|
|
|
if ut.Equals(model.NoneType) {
|
|
|
|
isOptional = true
|
|
|
|
}
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
switch ut := ut.(type) {
|
|
|
|
case *model.OpaqueType:
|
2022-12-12 23:18:35 +00:00
|
|
|
if isOptional {
|
|
|
|
return g.argumentTypeNamePtr(expr, ut, isInput)
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
return g.argumentTypeName(expr, ut, isInput)
|
|
|
|
case *model.ConstType:
|
|
|
|
return g.argumentTypeName(expr, ut.Type, isInput)
|
|
|
|
case *model.TupleType:
|
2020-06-18 20:34:22 +00:00
|
|
|
return g.argumentTypeName(expr, ut, isInput)
|
2021-04-16 12:04:42 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2020-05-28 20:47:49 +00:00
|
|
|
return "interface{}"
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.PromiseType:
|
2020-06-18 20:34:22 +00:00
|
|
|
return g.argumentTypeName(expr, destType.ElementType, isInput)
|
2020-05-26 14:07:54 +00:00
|
|
|
default:
|
2020-06-06 01:52:00 +00:00
|
|
|
contract.Failf("unexpected destType type %T", destType)
|
2020-05-26 14:07:54 +00:00
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2022-12-12 23:18:35 +00:00
|
|
|
func (g *generator) argumentTypeNamePtr(expr model.Expression, destType model.Type, isInput bool) (result string) {
|
|
|
|
res := g.argumentTypeName(expr, destType, isInput)
|
|
|
|
return "*" + res
|
|
|
|
}
|
|
|
|
|
2020-05-21 17:23:33 +00:00
|
|
|
func (g *generator) genRelativeTraversal(w io.Writer,
|
2023-03-03 16:36:39 +00:00
|
|
|
traversal hcl.Traversal, parts []model.Traversable, isRootResource bool,
|
|
|
|
) {
|
2020-06-18 00:19:39 +00:00
|
|
|
for i, part := range traversal {
|
2020-05-21 17:23:33 +00:00
|
|
|
var key cty.Value
|
|
|
|
switch part := part.(type) {
|
|
|
|
case hcl.TraverseAttr:
|
|
|
|
key = cty.StringVal(part.Name)
|
|
|
|
case hcl.TraverseIndex:
|
|
|
|
key = part.Key
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal part of type %T (%v)", part, part.SourceRange())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO handle optionals in go
|
|
|
|
// if model.IsOptionalType(model.GetTraversableType(parts[i])) {
|
|
|
|
// g.Fgen(w, "?")
|
|
|
|
// }
|
|
|
|
|
|
|
|
switch key.Type() {
|
|
|
|
case cty.String:
|
2020-06-18 00:19:39 +00:00
|
|
|
shouldConvert := isRootResource
|
|
|
|
if _, ok := parts[i].(*model.OutputType); ok {
|
|
|
|
shouldConvert = true
|
|
|
|
}
|
|
|
|
if key.AsString() == "id" && shouldConvert {
|
|
|
|
g.Fgenf(w, ".ID()")
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, ".%s", Title(key.AsString()))
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
case cty.Number:
|
|
|
|
idx, _ := key.AsBigFloat().Int64()
|
|
|
|
g.Fgenf(w, "[%d]", idx)
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal key of type %T (%v)", key, key.AsString())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type nameInfo int
|
|
|
|
|
|
|
|
func (nameInfo) Format(name string) string {
|
|
|
|
// TODO
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
// lowerExpression amends the expression with intrinsics for Go generation.
|
2021-06-24 16:17:55 +00:00
|
|
|
func (g *generator) lowerExpression(expr model.Expression, typ model.Type) (
|
2023-03-03 16:36:39 +00:00
|
|
|
model.Expression, []interface{},
|
|
|
|
) {
|
2021-09-30 03:11:56 +00:00
|
|
|
expr = pcl.RewritePropertyReferences(expr)
|
|
|
|
expr, diags := pcl.RewriteApplies(expr, nameInfo(0), false /*TODO*/)
|
2023-06-07 11:22:41 +00:00
|
|
|
expr, sTemps, splatDiags := g.rewriteSplat(expr, g.splatSpiller)
|
2023-11-03 14:22:43 +00:00
|
|
|
|
2022-07-07 20:15:47 +00:00
|
|
|
expr, convertDiags := pcl.RewriteConversions(expr, typ)
|
2020-06-16 06:00:02 +00:00
|
|
|
expr, tTemps, ternDiags := g.rewriteTernaries(expr, g.ternaryTempSpiller)
|
2021-06-24 16:17:55 +00:00
|
|
|
expr, jTemps, jsonDiags := g.rewriteToJSON(expr)
|
2020-06-16 06:00:02 +00:00
|
|
|
expr, rTemps, readDirDiags := g.rewriteReadDir(expr, g.readDirTempSpiller)
|
2020-06-23 17:59:26 +00:00
|
|
|
expr, oTemps, optDiags := g.rewriteOptionals(expr, g.optionalSpiller)
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2023-03-03 16:36:39 +00:00
|
|
|
bufferSize := len(tTemps) + len(jTemps) + len(rTemps) + len(sTemps) + len(oTemps)
|
2023-06-28 16:02:04 +00:00
|
|
|
temps := slice.Prealloc[interface{}](bufferSize)
|
2020-06-06 01:52:00 +00:00
|
|
|
for _, t := range tTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
|
|
|
for _, t := range jTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
for _, t := range rTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-18 00:19:39 +00:00
|
|
|
for _, t := range sTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-23 17:59:26 +00:00
|
|
|
for _, t := range oTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2022-07-07 20:15:47 +00:00
|
|
|
diags = append(diags, convertDiags...)
|
2020-05-28 20:47:49 +00:00
|
|
|
diags = append(diags, ternDiags...)
|
2020-06-06 01:52:00 +00:00
|
|
|
diags = append(diags, jsonDiags...)
|
2020-06-16 06:00:02 +00:00
|
|
|
diags = append(diags, readDirDiags...)
|
2020-06-18 00:19:39 +00:00
|
|
|
diags = append(diags, splatDiags...)
|
2020-06-23 17:59:26 +00:00
|
|
|
diags = append(diags, optDiags...)
|
2022-07-13 21:29:34 +00:00
|
|
|
g.diagnostics = g.diagnostics.Extend(diags)
|
2020-05-28 20:47:49 +00:00
|
|
|
return expr, temps
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genNYI(w io.Writer, reason string, vs ...interface{}) {
|
2023-06-02 15:17:59 +00:00
|
|
|
message := fmt.Sprintf("not yet implemented: %s", fmt.Sprintf(reason, vs...))
|
|
|
|
g.diagnostics = append(g.diagnostics, &hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagWarning,
|
|
|
|
Summary: message,
|
|
|
|
Detail: message,
|
|
|
|
})
|
2020-05-21 17:23:33 +00:00
|
|
|
g.Fgenf(w, "\"TODO: %s\"", fmt.Sprintf(reason, vs...))
|
|
|
|
}
|
2020-05-26 14:07:54 +00:00
|
|
|
|
|
|
|
func (g *generator) genApply(w io.Writer, expr *model.FunctionCallExpression) {
|
|
|
|
// Extract the list of outputs and the continuation expression from the `__apply` arguments.
|
2021-09-30 03:11:56 +00:00
|
|
|
applyArgs, then := pcl.ParseApplyCall(expr)
|
2020-05-26 14:07:54 +00:00
|
|
|
isInput := false
|
2020-06-18 20:34:22 +00:00
|
|
|
retType := g.argumentTypeName(nil, then.Signature.ReturnType, isInput)
|
2020-05-26 14:07:54 +00:00
|
|
|
// TODO account for outputs in other namespaces like aws
|
2021-11-18 22:50:51 +00:00
|
|
|
// TODO[pulumi/pulumi#8453] incomplete pattern code below.
|
|
|
|
var typeAssertion string
|
|
|
|
if retType == "[]string" {
|
|
|
|
typeAssertion = ".(pulumi.StringArrayOutput)"
|
|
|
|
} else {
|
2022-12-12 23:18:35 +00:00
|
|
|
if strings.HasPrefix(retType, "*") {
|
|
|
|
retType = Title(strings.TrimPrefix(retType, "*")) + "Ptr"
|
|
|
|
switch then.Body.(type) {
|
|
|
|
case *model.ScopeTraversalExpression:
|
|
|
|
traversal := then.Body.(*model.ScopeTraversalExpression)
|
|
|
|
traversal.RootName = "&" + traversal.RootName
|
|
|
|
then.Body = traversal
|
|
|
|
}
|
|
|
|
}
|
2021-11-18 22:50:51 +00:00
|
|
|
typeAssertion = fmt.Sprintf(".(%sOutput)", retType)
|
2022-05-20 17:29:05 +00:00
|
|
|
if !strings.Contains(retType, ".") {
|
2021-11-18 22:50:51 +00:00
|
|
|
typeAssertion = fmt.Sprintf(".(pulumi.%sOutput)", Title(retType))
|
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
}
|
2020-05-26 14:07:54 +00:00
|
|
|
|
|
|
|
if len(applyArgs) == 1 {
|
|
|
|
// If we only have a single output, just generate a normal `.Apply`
|
|
|
|
g.Fgenf(w, "%.v.ApplyT(%.v)%s", applyArgs[0], then, typeAssertion)
|
|
|
|
} else {
|
2020-06-18 00:19:39 +00:00
|
|
|
g.Fgenf(w, "pulumi.All(%.v", applyArgs[0])
|
|
|
|
applyArgs = applyArgs[1:]
|
|
|
|
for _, a := range applyArgs {
|
|
|
|
g.Fgenf(w, ",%.v", a)
|
|
|
|
}
|
2020-06-19 00:17:51 +00:00
|
|
|
allApplyThen, typeConvDecls := g.rewriteThenForAllApply(then)
|
|
|
|
g.Fgenf(w, ").ApplyT(")
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genAnonymousFunctionExpression(w, allApplyThen, typeConvDecls, true)
|
2020-06-19 00:17:51 +00:00
|
|
|
g.Fgenf(w, ")%s", typeAssertion)
|
2020-05-26 14:07:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-27 14:32:13 +00:00
|
|
|
|
2020-06-19 00:17:51 +00:00
|
|
|
// rewriteThenForAllApply rewrites an apply func after a .All replacing params with []interface{}
|
|
|
|
// other languages like javascript take advantage of destructuring to simplify All.Apply
|
|
|
|
// by generating something like [a1, a2, a3]
|
|
|
|
// Go doesn't support this syntax so we create a set of var decls with type assertions
|
|
|
|
// to prepend to the body: a1 := _args[0].(string) ... etc.
|
|
|
|
func (g *generator) rewriteThenForAllApply(
|
|
|
|
then *model.AnonymousFunctionExpression,
|
|
|
|
) (*model.AnonymousFunctionExpression, []string) {
|
2023-06-28 16:02:04 +00:00
|
|
|
typeConvDecls := slice.Prealloc[string](len(then.Parameters))
|
2020-06-19 00:17:51 +00:00
|
|
|
for i, v := range then.Parameters {
|
|
|
|
typ := g.argumentTypeName(nil, v.VariableType, false)
|
|
|
|
decl := fmt.Sprintf("%s := _args[%d].(%s)", v.Name, i, typ)
|
|
|
|
typeConvDecls = append(typeConvDecls, decl)
|
|
|
|
}
|
|
|
|
|
|
|
|
// dummy type that will produce []interface{} for argumentTypeName
|
|
|
|
interfaceArrayType := &model.TupleType{
|
|
|
|
ElementTypes: []model.Type{
|
|
|
|
model.BoolType, model.StringType, model.IntType,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
then.Parameters = []*model.Variable{{
|
|
|
|
Name: "_args",
|
|
|
|
VariableType: interfaceArrayType,
|
|
|
|
}}
|
|
|
|
then.Signature.Parameters = []model.Parameter{{
|
|
|
|
Name: "_args",
|
|
|
|
Type: interfaceArrayType,
|
|
|
|
}}
|
|
|
|
|
|
|
|
return then, typeConvDecls
|
|
|
|
}
|
|
|
|
|
2023-06-22 17:43:06 +00:00
|
|
|
// Writes a Go string literal.
|
|
|
|
// The literal will be a raw string literal if allowRaw is true
|
|
|
|
// and the string is long enough to benefit from it.
|
|
|
|
func (g *generator) genStringLiteral(w io.Writer, v string, allowRaw bool) {
|
|
|
|
// If the string is longer than 50 characters,
|
|
|
|
// contains at least 5 newlines,
|
|
|
|
// and does not contain a backtick,
|
|
|
|
// use a backtick string literal for readability.
|
|
|
|
canBeRaw := len(v) > 50 &&
|
|
|
|
strings.Count(v, "\n") >= 5 &&
|
|
|
|
!strings.Contains(v, "`")
|
|
|
|
if allowRaw && canBeRaw {
|
|
|
|
fmt.Fprintf(w, "`%s`", v)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
g.Fgen(w, "\"")
|
|
|
|
g.Fgen(w, g.escapeString(v))
|
|
|
|
g.Fgen(w, "\"")
|
2020-05-27 14:32:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) escapeString(v string) string {
|
|
|
|
builder := strings.Builder{}
|
|
|
|
for _, c := range v {
|
|
|
|
if c == '"' || c == '\\' {
|
|
|
|
builder.WriteRune('\\')
|
|
|
|
}
|
2020-06-18 20:34:22 +00:00
|
|
|
if c == '\n' {
|
|
|
|
builder.WriteRune('\\')
|
|
|
|
builder.WriteRune('n')
|
|
|
|
continue
|
|
|
|
}
|
2020-05-27 14:32:13 +00:00
|
|
|
builder.WriteRune(c)
|
|
|
|
}
|
|
|
|
return builder.String()
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2023-01-06 00:07:45 +00:00
|
|
|
//nolint:lll
|
2020-06-06 01:52:00 +00:00
|
|
|
func isInputty(destType model.Type) bool {
|
|
|
|
// TODO this needs to be more robust, likely the inverse of:
|
|
|
|
// https://github.com/pulumi/pulumi/blob/5330c97684cad78bcc60d8867f1b28704bd8a555/pkg/codegen/hcl2/model/type_eventuals.go#L244
|
|
|
|
switch destType := destType.(type) {
|
|
|
|
case *model.UnionType:
|
|
|
|
for _, t := range destType.ElementTypes {
|
|
|
|
if _, ok := t.(*model.OutputType); ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *model.OutputType:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) literalKey(x model.Expression) (string, bool) {
|
|
|
|
strKey := ""
|
|
|
|
switch x := x.(type) {
|
|
|
|
case *model.LiteralValueExpression:
|
2021-06-24 16:17:55 +00:00
|
|
|
if model.StringType.AssignableFrom(x.Type()) {
|
2020-06-06 01:52:00 +00:00
|
|
|
strKey = x.Value.AsString()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
g.GenLiteralValueExpression(&buf, x)
|
|
|
|
return buf.String(), true
|
|
|
|
case *model.TemplateExpression:
|
|
|
|
if len(x.Parts) == 1 {
|
2021-06-24 16:17:55 +00:00
|
|
|
if lit, ok := x.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) {
|
2020-06-06 01:52:00 +00:00
|
|
|
strKey = lit.Value.AsString()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2022-02-08 13:45:24 +00:00
|
|
|
return "", false
|
2020-06-06 01:52:00 +00:00
|
|
|
default:
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
return strKey, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// functionName computes the go package, module, and name for the given function token.
|
2020-06-18 20:34:22 +00:00
|
|
|
func (g *generator) functionName(tokenArg model.Expression) (string, string, string, hcl.Diagnostics) {
|
2020-06-06 01:52:00 +00:00
|
|
|
token := tokenArg.(*model.TemplateExpression).Parts[0].(*model.LiteralValueExpression).Value.AsString()
|
|
|
|
tokenRange := tokenArg.SyntaxNode().Range()
|
|
|
|
|
|
|
|
// Compute the resource type from the Pulumi type token.
|
2021-09-30 03:11:56 +00:00
|
|
|
pkg, module, member, diagnostics := pcl.DecomposeToken(token, tokenRange)
|
2020-06-06 01:52:00 +00:00
|
|
|
if strings.HasPrefix(member, "get") {
|
2020-06-18 20:34:22 +00:00
|
|
|
if g.useLookupInvokeForm(token) {
|
|
|
|
member = strings.Replace(member, "get", "lookup", 1)
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2022-08-30 00:16:22 +00:00
|
|
|
modOrAlias := g.getModOrAlias(pkg, module, module)
|
2023-02-23 20:51:11 +00:00
|
|
|
mod := strings.ReplaceAll(modOrAlias, "/", ".")
|
2020-10-06 05:57:00 +00:00
|
|
|
return pkg, mod, Title(member), diagnostics
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var functionPackages = map[string][]string{
|
2022-01-21 14:03:25 +00:00
|
|
|
"join": {"strings"},
|
|
|
|
"mimeType": {"mime", "path"},
|
2022-09-10 07:22:11 +00:00
|
|
|
"readDir": {"os"},
|
2023-01-06 22:39:16 +00:00
|
|
|
"readFile": {"os"},
|
|
|
|
"filebase64": {"encoding/base64", "os"},
|
2022-01-21 14:03:25 +00:00
|
|
|
"toBase64": {"encoding/base64"},
|
2022-09-16 23:12:29 +00:00
|
|
|
"fromBase64": {"encoding/base64"},
|
2022-01-21 14:03:25 +00:00
|
|
|
"toJSON": {"encoding/json"},
|
|
|
|
"sha1": {"fmt", "crypto/sha1"},
|
2023-01-06 22:39:16 +00:00
|
|
|
"filebase64sha256": {"fmt", "crypto/sha256", "os"},
|
2022-03-16 00:05:36 +00:00
|
|
|
"cwd": {"os"},
|
2023-06-11 09:05:10 +00:00
|
|
|
"singleOrNone": {"fmt"},
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genFunctionPackages(x *model.FunctionCallExpression) []string {
|
|
|
|
return functionPackages[x.Name]
|
|
|
|
}
|