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  }