mirror of https://github.com/pulumi/pulumi.git
118 lines
3.8 KiB
Go
118 lines
3.8 KiB
Go
// Copyright 2020-2024, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package python
|
|
|
|
import (
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen"
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model"
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/pcl"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
|
)
|
|
|
|
func isParameterReference(parameters codegen.Set, x model.Expression) bool {
|
|
scopeTraversal, ok := x.(*model.ScopeTraversalExpression)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return parameters.Has(scopeTraversal.Parts[0])
|
|
}
|
|
|
|
// parseProxyApply attempts to match and rewrite the given parsed apply using the following patterns:
|
|
//
|
|
// - __apply(<expr>, eval(x, x[index])) -> <expr>[index]
|
|
// - __apply(<expr>, eval(x, x.attr))) -> <expr>.attr
|
|
// - __apply(traversal, eval(x, x.attr)) -> traversal.attr
|
|
//
|
|
// Each of these patterns matches an apply that can be handled by `pulumi.Output`'s `__getitem__` or `__getattr__`
|
|
// method. The rewritten expressions will use those methods rather than calling `apply`.
|
|
func (g *generator) parseProxyApply(parameters codegen.Set, args []model.Expression,
|
|
then model.Expression,
|
|
) (model.Expression, bool) {
|
|
if len(args) != 1 {
|
|
return nil, false
|
|
}
|
|
|
|
arg := args[0]
|
|
switch then := then.(type) {
|
|
case *model.IndexExpression:
|
|
// Rewrite `__apply(<expr>, eval(x, x[index]))` to `<expr>[index]`.
|
|
if !isParameterReference(parameters, then.Collection) {
|
|
return nil, false
|
|
}
|
|
then.Collection = arg
|
|
case *model.ScopeTraversalExpression:
|
|
if !isParameterReference(parameters, then) {
|
|
return nil, false
|
|
}
|
|
|
|
switch arg := arg.(type) {
|
|
case *model.RelativeTraversalExpression:
|
|
arg.Traversal = append(arg.Traversal, then.Traversal[1:]...)
|
|
arg.Parts = append(arg.Parts, then.Parts...)
|
|
case *model.ScopeTraversalExpression:
|
|
arg.Traversal = append(arg.Traversal, then.Traversal[1:]...)
|
|
arg.Parts = append(arg.Parts, then.Parts...)
|
|
}
|
|
default:
|
|
return nil, false
|
|
}
|
|
|
|
diags := arg.Typecheck(false)
|
|
contract.Assertf(len(diags) == 0, "unexpected diagnostics: %v", diags)
|
|
return arg, true
|
|
}
|
|
|
|
// lowerProxyApplies lowers certain calls to the apply intrinsic into proxied property accesses. Concretely, this
|
|
// boils down to rewriting the following shapes
|
|
//
|
|
// - __apply(<expr>, eval(x, x[index]))
|
|
// - __apply(<expr>, eval(x, x.attr)))
|
|
// - __apply(scope.traversal, eval(x, x.attr))
|
|
//
|
|
// into (respectively)
|
|
//
|
|
// - <expr>[index]
|
|
// - <expr>.attr
|
|
// - scope.traversal.attr
|
|
//
|
|
// These forms will use `pulumi.Output`'s `__getitem__` and `__getattr__` instead of calling `apply`.
|
|
func (g *generator) lowerProxyApplies(expr model.Expression) (model.Expression, hcl.Diagnostics) {
|
|
rewriter := func(expr model.Expression) (model.Expression, hcl.Diagnostics) {
|
|
// Ignore the node if it is not a call to the apply intrinsic.
|
|
apply, ok := expr.(*model.FunctionCallExpression)
|
|
if !ok || apply.Name != pcl.IntrinsicApply {
|
|
return expr, nil
|
|
}
|
|
|
|
// Parse the apply call.
|
|
args, then := pcl.ParseApplyCall(apply)
|
|
|
|
parameters := codegen.Set{}
|
|
for _, p := range then.Parameters {
|
|
parameters.Add(p)
|
|
}
|
|
|
|
// Attempt to match (call __apply (rvar) (call __applyArg 0))
|
|
if v, ok := g.parseProxyApply(parameters, args, then.Body); ok {
|
|
return v, nil
|
|
}
|
|
|
|
return expr, nil
|
|
}
|
|
return model.VisitExpression(expr, model.IdentityVisitor, rewriter)
|
|
}
|