mirror of https://github.com/pulumi/pulumi.git
184 lines
6.7 KiB
Go
184 lines
6.7 KiB
Go
// Copyright 2016-2020, 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 dotnet
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen"
|
|
)
|
|
|
|
// isReservedWord returns true if s is a C# reserved word as per
|
|
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#keywords
|
|
func isReservedWord(s string) bool {
|
|
switch s {
|
|
case "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const",
|
|
"continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern",
|
|
"false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface",
|
|
"internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override",
|
|
"params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short",
|
|
"sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof",
|
|
"uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while":
|
|
return true
|
|
// Treat contextual keywords as keywords, as we don't validate the context around them.
|
|
case "add", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get",
|
|
"global", "group", "into", "join", "let", "nameof", "on", "orderby", "partial", "remove", "select", "set",
|
|
"unmanaged", "value", "var", "when", "where", "yield":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// isLegalIdentifierStart returns true if it is legal for c to be the first character of a C# identifier as per
|
|
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure
|
|
func isLegalIdentifierStart(c rune) bool {
|
|
return c == '_' || c == '@' ||
|
|
unicode.In(c, unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl)
|
|
}
|
|
|
|
// isLegalIdentifierPart returns true if it is legal for c to be part of a C# identifier (besides the first character)
|
|
// as per https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure
|
|
func isLegalIdentifierPart(c rune) bool {
|
|
return c == '_' ||
|
|
unicode.In(c, unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl, unicode.Mn, unicode.Mc,
|
|
unicode.Nd, unicode.Pc, unicode.Cf)
|
|
}
|
|
|
|
// makeValidIdentifier replaces characters that are not allowed in C# identifiers with underscores. A reserved word is
|
|
// prefixed with @. No attempt is made to ensure that the result is unique.
|
|
func makeValidIdentifier(name string) string {
|
|
var builder strings.Builder
|
|
for i, c := range name {
|
|
if i == 0 && c == '@' {
|
|
builder.WriteRune(c)
|
|
continue
|
|
}
|
|
if !isLegalIdentifierPart(c) {
|
|
builder.WriteRune('_')
|
|
} else {
|
|
if i == 0 && !isLegalIdentifierStart(c) {
|
|
builder.WriteRune('_')
|
|
}
|
|
builder.WriteRune(c)
|
|
}
|
|
}
|
|
name = builder.String()
|
|
if isReservedWord(name) {
|
|
return "@" + name
|
|
}
|
|
return name
|
|
}
|
|
|
|
// propertyName returns a name as a valid identifier in title case.
|
|
func propertyName(name string) string {
|
|
return makeValidIdentifier(Title(name))
|
|
}
|
|
|
|
func makeSafeEnumName(name, typeName string) (string, error) {
|
|
// Replace common single character enum names.
|
|
safeName := codegen.ExpandShortEnumName(name)
|
|
|
|
// If the name is one illegal character, return an error.
|
|
if len(safeName) == 1 && !isLegalIdentifierStart(rune(safeName[0])) {
|
|
return "", fmt.Errorf("enum name %s is not a valid identifier", safeName)
|
|
}
|
|
|
|
// Capitalize and make a valid identifier.
|
|
safeName = strings.Title(makeValidIdentifier(safeName))
|
|
|
|
// If there are multiple underscores in a row, replace with one.
|
|
regex := regexp.MustCompile(`_+`)
|
|
safeName = regex.ReplaceAllString(safeName, "_")
|
|
|
|
// If the enum name starts with an underscore, add the type name as a prefix.
|
|
if strings.HasPrefix(safeName, "_") {
|
|
safeName = typeName + safeName
|
|
}
|
|
|
|
// "Equals" conflicts with a method on the EnumType struct, change it to EqualsValue.
|
|
if safeName == "Equals" {
|
|
safeName = "EqualsValue"
|
|
}
|
|
|
|
return safeName, nil
|
|
}
|
|
|
|
// Provides code for a method which will be placed in the program preamble if deemed
|
|
// necessary. Because many Terraform functions are complex, it is much prettier to
|
|
// encapsulate them as their own function in the preamble.
|
|
func getHelperMethodIfNeeded(functionName string, indent string) (string, bool) {
|
|
switch functionName {
|
|
case "filebase64":
|
|
return fmt.Sprintf(`
|
|
%sstring ReadFileBase64(string path)
|
|
%s{
|
|
%s return Convert.ToBase64String(Encoding.UTF8.GetBytes(File.ReadAllText(path)));
|
|
%s}`, indent, indent, indent, indent), true
|
|
case "filebase64sha256":
|
|
return fmt.Sprintf(`
|
|
%sstring ComputeFileBase64Sha256(string path)
|
|
%s{
|
|
%s var fileData = Encoding.UTF8.GetBytes(File.ReadAllText(path));
|
|
%s var hashData = SHA256.Create().ComputeHash(fileData);
|
|
%s return Convert.ToBase64String(hashData);
|
|
%s}`, indent, indent, indent, indent, indent, indent), true
|
|
case "sha1":
|
|
return fmt.Sprintf(`
|
|
%sstring ComputeSHA1(string input)
|
|
%s{
|
|
%s var hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(input));
|
|
%s return BitConverter.ToString(hash).Replace("-","").ToLowerInvariant();
|
|
%s}`, indent, indent, indent, indent, indent), true
|
|
case "notImplemented":
|
|
return fmt.Sprintf(`
|
|
%sobject NotImplemented(string errorMessage)
|
|
%s{
|
|
%s throw new System.NotImplementedException(errorMessage);
|
|
%s}`, indent, indent, indent, indent), true
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
|
|
// LowerCamelCase sets the first character to lowercase
|
|
// LowerCamelCase("LowerCamelCase") -> "lowerCamelCase"
|
|
func LowerCamelCase(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
runes := []rune(s)
|
|
return string(append([]rune{unicode.ToLower(runes[0])}, runes[1:]...))
|
|
}
|
|
|
|
func extractNugetPackageNameAndVersion(nugetFilePath string) (string, string, bool) {
|
|
filename := filepath.Base(nugetFilePath)
|
|
parts := strings.Split(filename, ".")
|
|
if len(parts) >= 5 {
|
|
patch := parts[len(parts)-2]
|
|
minor := parts[len(parts)-3]
|
|
major := parts[len(parts)-4]
|
|
version := fmt.Sprintf("%s.%s.%s", major, minor, patch)
|
|
pkg := strings.TrimSuffix(filename, fmt.Sprintf(".%s.nupkg", version))
|
|
return pkg, version, true
|
|
}
|
|
|
|
return "", "", false
|
|
}
|