[csharp] Introducing the enumPropertyNaming option to the C# Generator (#16981)

* [csharp] Support the enumPropertyNaming option

* Add a doc comment to adjustNamingStyle
This commit is contained in:
Kazuhiro Fujieda 2023-11-07 11:52:10 +09:00 committed by GitHub
parent 65ccf0492c
commit 8e98671ba6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 106 additions and 3 deletions

View File

@ -45,6 +45,7 @@ import java.util.stream.Collectors;
import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
import static org.openapitools.codegen.utils.StringUtils.camelize; import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
public abstract class AbstractCSharpCodegen extends DefaultCodegen implements CodegenConfig { public abstract class AbstractCSharpCodegen extends DefaultCodegen implements CodegenConfig {
@ -84,6 +85,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
protected String sourceFolder = "src"; protected String sourceFolder = "src";
protected String invalidNamePrefix = "var"; protected String invalidNamePrefix = "var";
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.PascalCase;
// TODO: Add option for test folder output location. Nice to allow e.g. ./test instead of ./src. // TODO: Add option for test folder output location. Nice to allow e.g. ./test instead of ./src.
// This would require updating relative paths (e.g. path to main project file in test project file) // This would require updating relative paths (e.g. path to main project file in test project file)
@ -391,6 +393,10 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
setEnumValueSuffix(additionalProperties.get(CodegenConstants.ENUM_VALUE_SUFFIX).toString()); setEnumValueSuffix(additionalProperties.get(CodegenConstants.ENUM_VALUE_SUFFIX).toString());
} }
if (additionalProperties.containsKey(CodegenConstants.ENUM_PROPERTY_NAMING)) {
setEnumPropertyNaming((String) additionalProperties.get(CodegenConstants.ENUM_PROPERTY_NAMING));
}
// This either updates additionalProperties with the above fixes, or sets the default if the option was not specified. // This either updates additionalProperties with the above fixes, or sets the default if the option was not specified.
additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix); additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix);
@ -1598,6 +1604,22 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
this.supportNullable = supportNullable; this.supportNullable = supportNullable;
} }
public CodegenConstants.ENUM_PROPERTY_NAMING_TYPE getEnumPropertyNaming() {
return this.enumPropertyNaming;
}
public void setEnumPropertyNaming(final String enumPropertyNamingType) {
try {
this.enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.valueOf(enumPropertyNamingType);
} catch (IllegalArgumentException ex) {
StringBuilder sb = new StringBuilder(enumPropertyNamingType + " is an invalid enum property naming option. Please choose from:");
for (CodegenConstants.ENUM_PROPERTY_NAMING_TYPE t : CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.values()) {
sb.append("\n ").append(t.name());
}
throw new RuntimeException(sb.toString());
}
}
@Override @Override
public String toEnumValue(String value, String datatype) { public String toEnumValue(String value, String datatype) {
// C# only supports enums as literals for int, int?, long, long?, byte, and byte?. All else must be treated as strings. // C# only supports enums as literals for int, int?, long, long?, byte, and byte?. All else must be treated as strings.
@ -1622,12 +1644,12 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
@Override @Override
public String toEnumVarName(String name, String datatype) { public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) { if (name.length() == 0) {
return "Empty"; return adjustNamingStyle("Empty");
} }
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(name) != null) { if (getSymbolName(name) != null) {
return camelize(getSymbolName(name)); return adjustNamingStyle(getSymbolName(name));
} }
String enumName = sanitizeName(name); String enumName = sanitizeName(name);
@ -1635,7 +1657,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("^_", "");
enumName = enumName.replaceFirst("_$", ""); enumName = enumName.replaceFirst("_$", "");
enumName = camelize(enumName) + this.enumValueSuffix; enumName = adjustNamingStyle(enumName) + this.enumValueSuffix;
if (enumName.matches("\\d.*")) { // starts with number if (enumName.matches("\\d.*")) { // starts with number
return "_" + enumName; return "_" + enumName;
@ -1644,6 +1666,33 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
} }
} }
/**
* Adjust the naming style of a given name based on the enumPropertyNaming option.
*
* @param name The original name
* @return The adjusted name
*/
private String adjustNamingStyle(String name)
{
switch (getEnumPropertyNaming()) {
case original:
return name;
case camelCase:
// NOTE: Removes hyphens and underscores
return camelize(name, LOWERCASE_FIRST_LETTER);
case PascalCase:
// NOTE: Removes hyphens and underscores
return camelize(name);
case snake_case:
// NOTE: Removes hyphens
return underscore(name);
case UPPERCASE:
return underscore(name).toUpperCase(Locale.ROOT);
default:
return name;
}
}
@Override @Override
public String toEnumName(CodegenProperty property) { public String toEnumName(CodegenProperty property) {
return sanitizeName(camelize(property.name)) + this.enumNameSuffix; return sanitizeName(camelize(property.name)) + this.enumNameSuffix;

View File

@ -28,11 +28,15 @@ import org.openapitools.codegen.languages.AspNetServerCodegen;
import org.openapitools.codegen.languages.CSharpClientCodegen; import org.openapitools.codegen.languages.CSharpClientCodegen;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.openapitools.codegen.CodegenConstants.ENUM_PROPERTY_NAMING_TYPE;
import static org.openapitools.codegen.CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.*;
public class CSharpModelEnumTest { public class CSharpModelEnumTest {
// TODO there's no parent/child method in ComposeSchema so we will need to revise the code // TODO there's no parent/child method in ComposeSchema so we will need to revise the code
// before we can re-enable the test case below // before we can re-enable the test case below
@ -148,4 +152,54 @@ public class CSharpModelEnumTest {
Assert.assertEquals(codegen.toEnumVarName("Aaaa", ""), "Aaaa"); Assert.assertEquals(codegen.toEnumVarName("Aaaa", ""), "Aaaa");
} }
@Test(description = "support enum naming style options", dataProvider = "enumVarName")
public void testToEnumVarName(String name, String expected, ENUM_PROPERTY_NAMING_TYPE naming) throws Exception {
final AspNetServerCodegen codegen = new AspNetServerCodegen();
codegen.setEnumPropertyNaming(naming.name());
codegen.setEnumValueSuffix("");
Assert.assertEquals(codegen.toEnumVarName(name, "string"), expected);
}
@DataProvider(name = "enumVarName")
public Object[][] provideTestData()
{
return new Object[][] {
{ "FooBar", "fooBar", camelCase },
{ "fooBar", "fooBar", camelCase },
{ "foo-bar", "fooBar", camelCase },
{ "foo_bar", "fooBar", camelCase },
{ "foo bar", "fooBar", camelCase },
{ "FOO-BAR", "fOOBAR", camelCase }, // camelize doesn't support uppercase
{ "FOO_BAR", "fOOBAR", camelCase }, // ditto
{ "FooBar", "FooBar", PascalCase },
{ "fooBar", "FooBar", PascalCase },
{ "foo-bar", "FooBar", PascalCase },
{ "foo_bar", "FooBar", PascalCase },
{ "foo bar", "FooBar", PascalCase },
{ "FOO-BAR", "FOOBAR", PascalCase }, // ditto
{ "FOO_BAR", "FOOBAR", PascalCase }, // ditto
{ "FooBar", "foo_bar", snake_case },
{ "fooBar", "foo_bar", snake_case },
{ "foo-bar", "foo_bar", snake_case },
{ "foo_bar", "foo_bar", snake_case },
{ "foo bar", "foo_bar", snake_case },
{ "FOO-BAR", "foo_bar", snake_case },
{ "FOO_BAR", "foo_bar", snake_case },
{ "FooBar", "FOO_BAR", UPPERCASE },
{ "fooBar", "FOO_BAR", UPPERCASE },
{ "foo-bar", "FOO_BAR", UPPERCASE },
{ "foo_bar", "FOO_BAR", UPPERCASE },
{ "foo bar", "FOO_BAR", UPPERCASE },
{ "FOO-BAR", "FOO_BAR", UPPERCASE },
{ "FOO_BAR", "FOO_BAR", UPPERCASE },
{ "FooBar", "FooBar", original },
{ "fooBar", "fooBar", original },
{ "foo-bar", "foo_bar", original },
{ "foo_bar", "foo_bar", original },
{ "foo bar", "foo_bar", original },
{ "FOO-BAR", "FOO_BAR", original },
{ "FOO_BAR", "FOO_BAR", original },
};
}
} }