diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Extensions/StringExtensions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Extensions/StringExtensions.cs index 5c754e5fe48..53fe46f08cd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Extensions/StringExtensions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Extensions/StringExtensions.cs @@ -9,10 +9,11 @@ namespace Microsoft.TypeSpec.Generator.Input.Extensions { public static class StringExtensions { - private static bool IsWordSeparator(char c) => !SyntaxFacts.IsIdentifierPartCharacter(c) || c == '_'; + private static bool IsWordSeparator(char c, bool preserveUnderscores = false) + => !SyntaxFacts.IsIdentifierPartCharacter(c) || (!preserveUnderscores && c == '_'); [return: NotNullIfNotNull("name")] - public static string ToIdentifierName(this string name, bool useCamelCase = false) + public static string ToIdentifierName(this string name, bool useCamelCase = false, bool preserveUnderscores = false) { if (string.IsNullOrEmpty(name)) { @@ -39,7 +40,7 @@ public static string ToIdentifierName(this string name, bool useCamelCase = fals for (; i < name.Length; i++) { var c = name[i]; - if (IsWordSeparator(c)) + if (IsWordSeparator(c, preserveUnderscores)) { upperCase = true; continue; @@ -56,7 +57,7 @@ public static string ToIdentifierName(this string name, bool useCamelCase = fals upperCase = false; // grow the first word length when this letter follows by two other upper case letters // this happens in OSProfile, where OS is the first word - if (i + 2 < name.Length && char.IsUpper(name[i + 1]) && (char.IsUpper(name[i + 2]) || IsWordSeparator(name[i + 2]))) + if (i + 2 < name.Length && char.IsUpper(name[i + 1]) && (char.IsUpper(name[i + 2]) || IsWordSeparator(name[i + 2], preserveUnderscores))) firstWordLength++; // grow the first word length when this letter follows by another upper case letter and an end of the string // this happens when the string only has one word, like OS, DNS @@ -77,6 +78,6 @@ public static string ToIdentifierName(this string name, bool useCamelCase = fals } [return: NotNullIfNotNull(nameof(name))] - public static string ToVariableName(this string name) => name.ToIdentifierName(useCamelCase: true); + public static string ToVariableName(this string name, bool preserveUnderscores = false) => name.ToIdentifierName(useCamelCase: true, preserveUnderscores: preserveUnderscores); } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/StringExtensionsTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/StringExtensionsTests.cs new file mode 100644 index 00000000000..f6d04805531 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/StringExtensionsTests.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.TypeSpec.Generator.Input.Extensions; +using NUnit.Framework; + +namespace Microsoft.TypeSpec.Generator.Input.Tests +{ + public class StringExtensionsTests + { + // Existing behavior without preserveUnderscores (default) + [TestCase("Tls_1_0", false, "Tls10")] + [TestCase("hello_world", false, "HelloWorld")] + [TestCase("_leading", false, "Leading")] + [TestCase("trailing_", false, "Trailing")] + [TestCase("UPPER_CASE", false, "UPPERCASE")] + [TestCase("simple", false, "Simple")] + [TestCase("", false, "")] + [TestCase(null, false, null)] + // New behavior with preserveUnderscores = true + [TestCase("Tls_1_0", true, "Tls_1_0")] + [TestCase("hello_world", true, "Hello_world")] + [TestCase("_leading", true, "_leading")] + [TestCase("trailing_", true, "Trailing_")] + [TestCase("UPPER_CASE", true, "UPPER_CASE")] + [TestCase("simple", true, "Simple")] + [TestCase("", true, "")] + [TestCase(null, true, null)] + [TestCase("TLS_1_0", true, "TLS_1_0")] + [TestCase("foo__bar", true, "Foo__bar")] + public void TestToIdentifierNamePreserveUnderscores(string name, bool preserveUnderscores, string expected) + { + var result = name.ToIdentifierName(preserveUnderscores: preserveUnderscores); + Assert.AreEqual(expected, result); + } + + // Existing behavior of ToVariableName without preserveUnderscores + [TestCase("HelloWorld", false, "helloWorld")] + [TestCase("Tls_1_0", false, "tls10")] + [TestCase("UPPER_CASE", false, "upperCASE")] + // New behavior with preserveUnderscores = true + [TestCase("HelloWorld", true, "helloWorld")] + [TestCase("Tls_1_0", true, "tls_1_0")] + [TestCase("UPPER_CASE", true, "uppeR_CASE")] + public void TestToVariableNamePreserveUnderscores(string name, bool preserveUnderscores, string expected) + { + var result = name.ToVariableName(preserveUnderscores: preserveUnderscores); + Assert.AreEqual(expected, result); + } + } +}