github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/go/gen_program_optionals.go (about) 1 package gen 2 3 import ( 4 "github.com/hashicorp/hcl/v2" 5 "github.com/hashicorp/hcl/v2/hclsyntax" 6 "github.com/pulumi/pulumi/pkg/v3/codegen" 7 "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model" 8 "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax" 9 "github.com/pulumi/pulumi/pkg/v3/codegen/pcl" 10 "github.com/pulumi/pulumi/pkg/v3/codegen/schema" 11 ) 12 13 type optionalTemp struct { 14 Name string 15 Value model.Expression 16 } 17 18 func (ot *optionalTemp) Type() model.Type { 19 return ot.Value.Type() 20 } 21 22 func (ot *optionalTemp) Traverse(traverser hcl.Traverser) (model.Traversable, hcl.Diagnostics) { 23 return ot.Type().Traverse(traverser) 24 } 25 26 func (ot *optionalTemp) SyntaxNode() hclsyntax.Node { 27 return syntax.None 28 } 29 30 type optionalSpiller struct { 31 invocation *model.FunctionCallExpression 32 intrinsicConvertTo *model.Type 33 } 34 35 func (os *optionalSpiller) preVisitor(x model.Expression) (model.Expression, hcl.Diagnostics) { 36 switch x := x.(type) { 37 case *model.FunctionCallExpression: 38 if x.Name == "invoke" { 39 // recurse into invoke args 40 isOutputInvoke, _, _ := pcl.RecognizeOutputVersionedInvoke(x) 41 // ignore output-versioned invokes as they do not need converting 42 if !isOutputInvoke { 43 os.invocation = x 44 } 45 os.intrinsicConvertTo = nil 46 return x, nil 47 } 48 if x.Name == pcl.IntrinsicConvert { 49 if os.invocation != nil { 50 os.intrinsicConvertTo = &x.Signature.ReturnType 51 } 52 return x, nil 53 } 54 case *model.ObjectConsExpression: 55 if os.invocation == nil { 56 return x, nil 57 } 58 destType := x.Type() 59 if os.intrinsicConvertTo != nil { 60 destType = *os.intrinsicConvertTo 61 } 62 if schemaType, ok := pcl.GetSchemaForType(destType); ok { 63 if schemaType, ok := schemaType.(*schema.ObjectType); ok { 64 // map of item name to optional type wrapper fn 65 optionalPrimitives := make(map[string]schema.Type) 66 for _, v := range schemaType.Properties { 67 if !v.IsRequired() { 68 ty := codegen.UnwrapType(v.Type) 69 switch ty { 70 case schema.NumberType, schema.BoolType, schema.IntType, schema.StringType: 71 optionalPrimitives[v.Name] = ty 72 } 73 } 74 } 75 for i, item := range x.Items { 76 // keys for schematized objects should be simple strings 77 if key, ok := item.Key.(*model.LiteralValueExpression); ok { 78 if model.StringType.AssignableFrom(key.Type()) { 79 strKey := key.Value.AsString() 80 if schemaType, isOptional := optionalPrimitives[strKey]; isOptional { 81 functionName := os.getOptionalConversion(schemaType) 82 expectedModelType := os.getExpectedModelType(schemaType) 83 84 x.Items[i].Value = &model.FunctionCallExpression{ 85 Name: functionName, 86 Signature: model.StaticFunctionSignature{ 87 Parameters: []model.Parameter{{ 88 Name: "val", 89 Type: expectedModelType, 90 }}, 91 ReturnType: model.NewOptionalType(expectedModelType), 92 }, 93 Args: []model.Expression{item.Value}, 94 } 95 } 96 } 97 } 98 } 99 } 100 } 101 // Clear before visiting children, require another __convert call to set again 102 os.intrinsicConvertTo = nil 103 return x, nil 104 default: 105 // Ditto 106 os.intrinsicConvertTo = nil 107 return x, nil 108 } 109 return x, nil 110 } 111 112 func (os *optionalSpiller) postVisitor(x model.Expression) (model.Expression, hcl.Diagnostics) { 113 switch x := x.(type) { 114 case *model.FunctionCallExpression: 115 if x.Name == "invoke" { 116 if x == os.invocation { 117 // Clear invocation flag once we're done traversing children. 118 os.invocation = nil 119 } 120 } 121 } 122 return x, nil 123 } 124 125 func (*optionalSpiller) getOptionalConversion(ty schema.Type) string { 126 switch ty { 127 case schema.NumberType: 128 return "goOptionalFloat64" 129 case schema.BoolType: 130 return "goOptionalBool" 131 case schema.IntType: 132 return "goOptionalInt" 133 case schema.StringType: 134 return "goOptionalString" 135 default: 136 return "" 137 } 138 } 139 140 func (*optionalSpiller) getExpectedModelType(ty schema.Type) model.Type { 141 switch ty { 142 case schema.NumberType: 143 return model.NumberType 144 case schema.BoolType: 145 return model.BoolType 146 case schema.IntType: 147 return model.IntType 148 case schema.StringType: 149 return model.StringType 150 default: 151 return nil 152 } 153 } 154 155 func (g *generator) rewriteOptionals( 156 x model.Expression, 157 spiller *optionalSpiller, 158 ) (model.Expression, []*optionalTemp, hcl.Diagnostics) { 159 // We want to recurse but we only want to use the previsitor, if post visitor is nil we don't 160 // recurse. 161 x, diags := model.VisitExpression(x, spiller.preVisitor, spiller.postVisitor) 162 163 return x, nil, diags 164 }