[codegen/dotnet,nodejs] - Improve makeSafeEnumName (#5711)

* Improve makeSafeEnumName

* Improve nodejs enumName generator

* Fix comment

* PR feedback
This commit is contained in:
Komal 2020-11-09 11:33:22 -08:00 committed by GitHub
parent 64e5de1edc
commit a259d59624
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 170 additions and 29 deletions

View file

@ -1008,17 +1008,24 @@ func printObsoleteAttribute(w io.Writer, deprecationMessage, indent string) {
}
}
func makeSafeEnumName(enum *schema.Enum) string {
if enum.Name != "" {
return enum.Name
}
return strings.Title(makeValidIdentifier(enum.Value.(string)))
}
func (mod *modContext) genEnum(w io.Writer, enum *schema.EnumType) error {
indent := " "
enumName := tokenToName(enum.Token)
// Fix up identifiers for each enum value.
for _, e := range enum.Elements {
// If the enum doesn't have a name, set the value as the name.
if e.Name == "" {
e.Name = fmt.Sprintf("%v", e.Value)
}
safeName, err := makeSafeEnumName(e.Name)
if err != nil {
return err
}
e.Name = safeName
}
// Print documentation comment
printComment(w, enum.Comment, indent)
@ -1050,7 +1057,6 @@ func (mod *modContext) genEnum(w io.Writer, enum *schema.EnumType) error {
for _, e := range enum.Elements {
printComment(w, e.Comment, indent)
printObsoleteAttribute(w, e.DeprecationMessage, indent)
e.Name = makeSafeEnumName(e)
fmt.Fprintf(w, "%[1]spublic static %[2]s %[3]s { get; } = new %[2]s(", indent, enumName, e.Name)
if enum.ElementType == schema.StringType {
fmt.Fprintf(w, "%q", e.Value)
@ -1109,7 +1115,6 @@ func (mod *modContext) genEnum(w io.Writer, enum *schema.EnumType) error {
indent := strings.Repeat(indent, 2)
printComment(w, e.Comment, indent)
printObsoleteAttribute(w, e.DeprecationMessage, indent)
e.Name = makeSafeEnumName(e)
fmt.Fprintf(w, "%s%s = %v,\n", indent, e.Name, e.Value)
}
default:

View file

@ -49,3 +49,33 @@ func TestGeneratePackage(t *testing.T) {
})
}
}
func TestMakeSafeEnumName(t *testing.T) {
tests := []struct {
input string
expected string
wantErr bool
}{
{"+", "", true},
{"*", "Asterisk", false},
{"0", "Zero", false},
{"Microsoft-Windows-Shell-Startup", "Microsoft_Windows_Shell_Startup", false},
{"Microsoft.Batch", "Microsoft_Batch", false},
{"readonly", "@Readonly", false},
{"SystemAssigned, UserAssigned", "SystemAssigned_UserAssigned", false},
{"Dev(NoSLA)_Standard_D11_v2", "Dev_NoSLA_Standard_D11_v2", false},
{"Standard_E8as_v4+1TB_PS", "Standard_E8as_v4_1TB_PS", false},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got, err := makeSafeEnumName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("makeSafeEnumName() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.expected {
t.Errorf("makeSafeEnumName() got = %v, want %v", got, tt.expected)
}
})
}
}

View file

@ -15,8 +15,11 @@
package dotnet
import (
"regexp"
"strings"
"unicode"
"github.com/pkg/errors"
)
// isReservedWord returns true if s is a C# reserved word as per
@ -78,3 +81,45 @@ func makeValidIdentifier(name string) string {
func propertyName(name string) string {
return makeValidIdentifier(Title(name))
}
func makeSafeEnumName(name string) (string, error) {
safeName := name
// If the name is one illegal character, replace it.
if len(safeName) == 1 && !isLegalIdentifierStart(rune(safeName[0])) {
enumReplacer := strings.NewReplacer(
"0", "Zero",
"1", "One",
"2", "Two",
"3", "Three",
"4", "Four",
"5", "Five",
"6", "Six",
"7", "Seven",
"8", "Eight",
"9", "Nine",
"*", "Asterisk",
)
safeName = enumReplacer.Replace(safeName)
// If it's still an illegal character (we weren't able to find a replacement), return an error.
if !isLegalIdentifierStart(rune(safeName[0])) {
return "", errors.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, "_")
// "Equals" conflicts with a method on the EnumType struct, change it to EqualsValue.
if safeName == "Equals" {
safeName = "EqualsValue"
}
return safeName, nil
}

View file

@ -1111,19 +1111,21 @@ func (mod *modContext) genNamespace(w io.Writer, ns *namespace, input bool, leve
}
}
func makeSafeEnumName(name string) string {
return makeValidIdentifier(title(name))
}
func (mod *modContext) genEnum(w io.Writer, enum *schema.EnumType) {
func (mod *modContext) genEnum(w io.Writer, enum *schema.EnumType) error {
indent := " "
enumName := tokenToName(enum.Token)
fmt.Fprintf(w, "export const %s = {\n", enumName)
for _, e := range enum.Elements {
// If the enum doesn't have a name, set the value as the name.
if e.Name == "" {
e.Name = fmt.Sprintf("%v", e.Value)
}
e.Name = makeSafeEnumName(e.Name)
safeName, err := makeSafeEnumName(e.Name)
if err != nil {
return err
}
e.Name = safeName
printComment(w, e.Comment, e.DeprecationMessage, indent)
fmt.Fprintf(w, "%s%s: ", indent, e.Name)
if val, ok := e.Value.(string); ok {
@ -1137,6 +1139,7 @@ func (mod *modContext) genEnum(w io.Writer, enum *schema.EnumType) {
printComment(w, enum.Comment, "", "")
fmt.Fprintf(w, "export type %[1]s = (typeof %[1]s)[keyof typeof %[1]s];\n", enumName)
return nil
}
type fs map[string][]byte
@ -1249,7 +1252,10 @@ func (mod *modContext) gen(fs fs) error {
buffer := &bytes.Buffer{}
mod.genHeader(buffer, []string{}, nil)
mod.genEnums(buffer, mod.enums)
err := mod.genEnums(buffer, mod.enums)
if err != nil {
return err
}
var fileName string
if modDir == "" {
@ -1378,7 +1384,7 @@ func (mod *modContext) hasEnums() bool {
return false
}
func (mod *modContext) genEnums(buffer *bytes.Buffer, enums []*schema.EnumType) {
func (mod *modContext) genEnums(buffer *bytes.Buffer, enums []*schema.EnumType) error {
if len(mod.children) > 0 {
children := codegen.NewStringSet()
@ -1402,12 +1408,16 @@ func (mod *modContext) genEnums(buffer *bytes.Buffer, enums []*schema.EnumType)
if len(enums) > 0 {
fmt.Fprintf(buffer, "\n")
for i, enum := range enums {
mod.genEnum(buffer, enum)
err := mod.genEnum(buffer, enum)
if err != nil {
return err
}
if i != len(enums)-1 {
fmt.Fprintf(buffer, "\n")
}
}
}
return nil
}
// genPackageMetadata generates all the non-code metadata required by a Pulumi package.

View file

@ -59,19 +59,30 @@ func TestMakeSafeEnumName(t *testing.T) {
tests := []struct {
input string
expected string
wantErr bool
}{
{"red", "Red"},
{"snake_cased_name", "Snake_cased_name"},
{"8", "_8"},
{"Microsoft-Windows-Shell-Startup", "Microsoft_Windows_Shell_Startup"},
{"Microsoft.Batch", "Microsoft_Batch"},
{"readonly", "Readonly"},
{"SystemAssigned, UserAssigned", "SystemAssigned__UserAssigned"},
{"Dev(NoSLA)_Standard_D11_v2", "Dev_NoSLA__Standard_D11_v2"},
{"Standard_E8as_v4+1TB_PS", "Standard_E8as_v4_1TB_PS"},
{"red", "Red", false},
{"snake_cased_name", "Snake_cased_name", false},
{"+", "", true},
{"*", "Asterisk", false},
{"0", "Zero", false},
{"Microsoft-Windows-Shell-Startup", "Microsoft_Windows_Shell_Startup", false},
{"Microsoft.Batch", "Microsoft_Batch", false},
{"readonly", "Readonly", false},
{"SystemAssigned, UserAssigned", "SystemAssigned_UserAssigned", false},
{"Dev(NoSLA)_Standard_D11_v2", "Dev_NoSLA_Standard_D11_v2", false},
{"Standard_E8as_v4+1TB_PS", "Standard_E8as_v4_1TB_PS", false},
}
for _, tt := range tests {
result := makeSafeEnumName(tt.input)
assert.Equal(t, tt.expected, result)
t.Run(tt.input, func(t *testing.T) {
got, err := makeSafeEnumName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("makeSafeEnumName() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.expected {
t.Errorf("makeSafeEnumName() got = %v, want %v", got, tt.expected)
}
})
}
}

View file

@ -16,8 +16,11 @@ package nodejs
import (
"io"
"regexp"
"strings"
"unicode"
"github.com/pkg/errors"
)
// isReservedWord returns true if s is a reserved word as per ECMA-262.
@ -98,3 +101,40 @@ func makeValidIdentifier(name string) string {
}
return name
}
func makeSafeEnumName(name string) (string, error) {
safeName := name
// If the name is one illegal character, replace it.
if len(safeName) == 1 && !isLegalIdentifierStart(rune(safeName[0])) {
enumReplacer := strings.NewReplacer(
"0", "Zero",
"1", "One",
"2", "Two",
"3", "Three",
"4", "Four",
"5", "Five",
"6", "Six",
"7", "Seven",
"8", "Eight",
"9", "Nine",
"*", "Asterisk",
)
safeName = enumReplacer.Replace(safeName)
// If it's still an illegal character (we weren't able to find a replacement), return an error.
if !isLegalIdentifierStart(rune(safeName[0])) {
return "", errors.Errorf("enum name %s is not a valid identifier", safeName)
}
}
// Capitalize and make a valid identifier.
safeName = makeValidIdentifier(title(safeName))
// If there are multiple underscores in a row, replace with one.
regex := regexp.MustCompile(`_+`)
safeName = regex.ReplaceAllString(safeName, "_")
return safeName, nil
}