github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/go/utilities.go (about) 1 // Copyright 2016-2020, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gen 16 17 import ( 18 "fmt" 19 "regexp" 20 "strings" 21 "unicode" 22 23 "github.com/pulumi/pulumi/pkg/v3/codegen" 24 "github.com/pulumi/pulumi/pkg/v3/codegen/cgstrings" 25 "github.com/pulumi/pulumi/pkg/v3/codegen/schema" 26 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 27 ) 28 29 // isReservedWord returns true if s is a Go reserved word as per 30 // https://golang.org/ref/spec#Keywords 31 func isReservedWord(s string) bool { 32 switch s { 33 case "break", "default", "func", " interface", "select", 34 "case", "defer", "go", "map", "struct", 35 "chan", "else", "goto", "package", "switch", 36 "const", "fallthrough", "if", "range", "type", 37 "continue", "for", "import", "return", "var": 38 return true 39 40 default: 41 return false 42 } 43 } 44 45 // isReservedResourceField returns true if s would conflict with a method on a generated 46 // resource. 47 func isReservedResourceField(resourceName, s string) bool { 48 switch s { 49 case "ID", "URN", "GetProvider", "ElementType": 50 return true 51 default: 52 if resourceName != "" { 53 toOutput := "To" + resourceName + "Output" 54 return s == toOutput || s == toOutput+"WithContext" 55 } 56 return false 57 } 58 } 59 60 // isLegalIdentifierStart returns true if it is legal for c to be the first character of a Go identifier as per 61 // https://golang.org/ref/spec#Identifiers 62 func isLegalIdentifierStart(c rune) bool { 63 return c == '_' || unicode.In(c, unicode.Letter) 64 } 65 66 // isLegalIdentifierPart returns true if it is legal for c to be part of a Go identifier (besides the first character) 67 // https://golang.org/ref/spec#Identifiers 68 func isLegalIdentifierPart(c rune) bool { 69 return c == '_' || 70 unicode.In(c, unicode.Letter, unicode.Digit) 71 } 72 73 // makeValidIdentifier replaces characters that are not allowed in Go identifiers with underscores. A reserved word is 74 // prefixed with _. No attempt is made to ensure that the result is unique. 75 func makeValidIdentifier(name string) string { 76 var builder strings.Builder 77 for i, c := range name { 78 // ptr dereference 79 if i == 0 && c == '&' { 80 builder.WriteRune(c) 81 continue 82 } 83 if !isLegalIdentifierPart(c) { 84 builder.WriteRune('_') 85 } else { 86 if i == 0 && !isLegalIdentifierStart(c) { 87 builder.WriteRune('_') 88 } 89 builder.WriteRune(c) 90 } 91 } 92 name = builder.String() 93 if isReservedWord(name) { 94 return "_" + name 95 } 96 return name 97 } 98 99 func makeSafeEnumName(name, typeName string) (string, error) { 100 safeName := codegen.ExpandShortEnumName(name) 101 102 // If the name is one illegal character, return an error. 103 if len(safeName) == 1 && !isLegalIdentifierStart(rune(safeName[0])) { 104 return "", fmt.Errorf("enum name %s is not a valid identifier", safeName) 105 } 106 107 // Capitalize and make a valid identifier. 108 safeName = enumTitle(safeName) 109 safeName = makeValidIdentifier(safeName) 110 111 // If there are multiple underscores in a row, replace with one. 112 regex := regexp.MustCompile(`_+`) 113 safeName = regex.ReplaceAllString(safeName, "_") 114 115 // Add the type to the name to disambiguate constants used for enum values 116 if strings.Contains(safeName, "_") && !strings.HasPrefix(safeName, "_") { 117 safeName = fmt.Sprintf("_%s", safeName) 118 } 119 120 safeName = typeName + safeName 121 122 return safeName, nil 123 } 124 125 // Title converts the input string to a title case 126 // where only the initial letter is upper-cased. 127 // It also removes $-prefix if any. 128 func enumTitle(s string) string { 129 if s == "" { 130 return "" 131 } 132 if s[0] == '$' { 133 return Title(s[1:]) 134 } 135 s = cgstrings.UppercaseFirst(s) 136 return cgstrings.ModifyStringAroundDelimeter(s, "-", func(next string) string { 137 return "_" + cgstrings.UppercaseFirst(next) 138 }) 139 } 140 141 // Calculate the name of a field in a resource 142 func fieldName(pkg *pkgContext, r *schema.Resource, p *schema.Property) string { 143 s := Title(p.Name) 144 var name string 145 if r != nil { 146 name = disambiguatedResourceName(r, pkg) 147 } 148 if !isReservedResourceField(name, s) { 149 return s 150 } 151 152 res := s + "_" 153 contract.Assert(!isReservedResourceField(name, res)) 154 return res 155 }