github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/dotnet/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 dotnet 16 17 import ( 18 "fmt" 19 "regexp" 20 "strings" 21 "unicode" 22 23 "github.com/pulumi/pulumi/pkg/v3/codegen" 24 ) 25 26 // isReservedWord returns true if s is a C# reserved word as per 27 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#keywords 28 func isReservedWord(s string) bool { 29 switch s { 30 case "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", 31 "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", 32 "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", 33 "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override", 34 "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", 35 "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof", 36 "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while": 37 return true 38 // Treat contextual keywords as keywords, as we don't validate the context around them. 39 case "add", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get", 40 "global", "group", "into", "join", "let", "nameof", "on", "orderby", "partial", "remove", "select", "set", 41 "unmanaged", "value", "var", "when", "where", "yield": 42 return true 43 default: 44 return false 45 } 46 } 47 48 // isLegalIdentifierStart returns true if it is legal for c to be the first character of a C# identifier as per 49 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure 50 func isLegalIdentifierStart(c rune) bool { 51 return c == '_' || c == '@' || 52 unicode.In(c, unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl) 53 } 54 55 // isLegalIdentifierPart returns true if it is legal for c to be part of a C# identifier (besides the first character) 56 // as per https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure 57 func isLegalIdentifierPart(c rune) bool { 58 return c == '_' || 59 unicode.In(c, unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl, unicode.Mn, unicode.Mc, 60 unicode.Nd, unicode.Pc, unicode.Cf) 61 } 62 63 // makeValidIdentifier replaces characters that are not allowed in C# identifiers with underscores. A reserved word is 64 // prefixed with @. No attempt is made to ensure that the result is unique. 65 func makeValidIdentifier(name string) string { 66 var builder strings.Builder 67 for i, c := range name { 68 if i == 0 && c == '@' { 69 builder.WriteRune(c) 70 continue 71 } 72 if !isLegalIdentifierPart(c) { 73 builder.WriteRune('_') 74 } else { 75 if i == 0 && !isLegalIdentifierStart(c) { 76 builder.WriteRune('_') 77 } 78 builder.WriteRune(c) 79 } 80 } 81 name = builder.String() 82 if isReservedWord(name) { 83 return "@" + name 84 } 85 return name 86 } 87 88 // propertyName returns a name as a valid identifier in title case. 89 func propertyName(name string) string { 90 return makeValidIdentifier(Title(name)) 91 } 92 93 func makeSafeEnumName(name, typeName string) (string, error) { 94 // Replace common single character enum names. 95 safeName := codegen.ExpandShortEnumName(name) 96 97 // If the name is one illegal character, return an error. 98 if len(safeName) == 1 && !isLegalIdentifierStart(rune(safeName[0])) { 99 return "", fmt.Errorf("enum name %s is not a valid identifier", safeName) 100 } 101 102 // Capitalize and make a valid identifier. 103 safeName = strings.Title(makeValidIdentifier(safeName)) 104 105 // If there are multiple underscores in a row, replace with one. 106 regex := regexp.MustCompile(`_+`) 107 safeName = regex.ReplaceAllString(safeName, "_") 108 109 // If the enum name starts with an underscore, add the type name as a prefix. 110 if strings.HasPrefix(safeName, "_") { 111 safeName = typeName + safeName 112 } 113 114 // "Equals" conflicts with a method on the EnumType struct, change it to EqualsValue. 115 if safeName == "Equals" { 116 safeName = "EqualsValue" 117 } 118 119 return safeName, nil 120 } 121 122 // Provides code for a method which will be placed in the program preamble if deemed 123 // necessary. Because many Terraform functions are complex, it is much prettier to 124 // encapsulate them as their own function in the preamble. 125 func getHelperMethodIfNeeded(functionName string) (string, bool) { 126 switch functionName { 127 case "filebase64": 128 return `private static string ReadFileBase64(string path) { 129 return Convert.ToBase64String(Encoding.UTF8.GetBytes(File.ReadAllText(path))) 130 }`, true 131 case "filebase64sha256": 132 return `private static string ComputeFileBase64Sha256(string path) { 133 var fileData = Encoding.UTF8.GetBytes(File.ReadAllText(path)); 134 var hashData = SHA256.Create().ComputeHash(fileData); 135 return Convert.ToBase64String(hashData); 136 }`, true 137 case "sha1": 138 return `private static string ComputeSHA1(string input) { 139 return BitConverter.ToString( 140 SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(input)) 141 ).Replace("-","").ToLowerInvariant()); 142 }`, true 143 default: 144 return "", false 145 } 146 } 147 148 // LowerCamelCase sets the first character to lowercase 149 // LowerCamelCase("LowerCamelCase") -> "lowerCamelCase" 150 func LowerCamelCase(s string) string { 151 if s == "" { 152 return "" 153 } 154 runes := []rune(s) 155 return string(append([]rune{unicode.ToLower(runes[0])}, runes[1:]...)) 156 }