Skip to content

Commit

Permalink
Fixed snake case name translation
Browse files Browse the repository at this point in the history
  • Loading branch information
YohDeadfall committed Feb 8, 2019
1 parent 064fa94 commit 7c8a37a
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 52 deletions.
88 changes: 39 additions & 49 deletions src/Npgsql/NameTranslation/NpgsqlSnakeCaseNameTranslator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Npgsql.NameTranslation
Expand Down Expand Up @@ -38,74 +39,63 @@ public string TranslateMemberName(string clrName) => LegacyMode
/// <summary>
/// Converts a string to its snake_case equivalent.
/// </summary>
/// <remarks>
/// Code borrowed from Newtonsoft.Json.
/// See https://github.com/JamesNK/Newtonsoft.Json/blob/f012ba857f36fe75b1294a210b9104130a4db4d5/Src/Newtonsoft.Json/Utilities/StringUtils.cs#L200-L276.
/// </remarks>
/// <param name="value">The value to convert.</param>
public static string ConvertToSnakeCase(string value)
{
if (string.IsNullOrEmpty(value))
return value;

var sb = new StringBuilder();
var state = SnakeCaseState.Start;
const char underscore = '_';
const UnicodeCategory noneCategory = UnicodeCategory.Control;

for (var i = 0; i < value.Length; i++)
var builder = new StringBuilder();
var previousCategory = noneCategory;

for (var currentIndex = 0; currentIndex< value.Length; currentIndex++)
{
if (value[i] == ' ')
var currentChar = value[currentIndex];
if (currentChar == underscore)
{
if (state != SnakeCaseState.Start)
state = SnakeCaseState.NewWord;
builder.Append(underscore);
previousCategory = noneCategory;
continue;
}
else if (char.IsUpper(value[i]))

var currentCategory = char.GetUnicodeCategory(currentChar);
switch (currentCategory)
{
switch (state)
{
case SnakeCaseState.Upper:
var hasNext = (i + 1 < value.Length);
if (i > 0 && hasNext)
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.TitlecaseLetter:
if (previousCategory == UnicodeCategory.SpaceSeparator ||
previousCategory == UnicodeCategory.LowercaseLetter ||
previousCategory != UnicodeCategory.DecimalDigitNumber &&
currentIndex > 0 &&
currentIndex + 1 < value.Length &&
char.IsLower(value[currentIndex + 1]))
{
var nextChar = value[i + 1];
if (!char.IsUpper(nextChar) && nextChar != '_')
{
sb.Append('_');
}
builder.Append(underscore);
}

currentChar = char.ToLower(currentChar);
break;
case SnakeCaseState.Lower:
case SnakeCaseState.NewWord:
sb.Append('_');

case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.DecimalDigitNumber:
if (previousCategory == UnicodeCategory.SpaceSeparator)
builder.Append(underscore);
break;
}

sb.Append(char.ToLowerInvariant(value[i]));
state = SnakeCaseState.Upper;
}
else if (value[i] == '_')
{
sb.Append('_');
state = SnakeCaseState.Start;
default:
if (previousCategory != noneCategory)
previousCategory = UnicodeCategory.SpaceSeparator;
continue;
}
else
{
if (state == SnakeCaseState.NewWord)
sb.Append('_');

sb.Append(value[i]);
state = SnakeCaseState.Lower;
}
builder.Append(currentChar);
previousCategory = currentCategory;
}

return sb.ToString();
}

enum SnakeCaseState
{
Start,
Lower,
Upper,
NewWord
return builder.ToString();
}
}
}
6 changes: 3 additions & 3 deletions test/Npgsql.Tests/SnakeCaseNameTranslatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ public string TranslateMemberName(string value, bool legacyMode)

static IEnumerable<TestCaseData> TestCases => new (string value, string legacyResult, string result)[]
{
("Hi!! This is text. Time to test.", "hi!! _this is text. _time to test.", "hi!!_this_is_text._time_to_test."),
("9999-12-31T23:59:59.9999999Z", "9999-12-31_t23:59:59.9999999_z", "9999-12-31_t23:59:59.9999999_z"),
("Hi!! This is text. Time to test.", "hi!! _this is text. _time to test.", "hi_this_is_text_time_to_test"),
("9999-12-31T23:59:59.9999999Z", "9999-12-31_t23:59:59.9999999_z", "9999_12_31t23_59_59_9999999z"),
("already_snake_case_ ", "already_snake_case_ ", "already_snake_case_"),
("SHOUTING_CASE", "s_h_o_u_t_i_n_g__c_a_s_e", "shouting_case"),
("IsJSONProperty", "is_j_s_o_n_property", "is_json_property"),
("SnA__ kEcAsE", "sn_a__ k_ec_as_e", "sn_a__k_ec_as_e"),
("SnA__kEcAsE", "sn_a__k_ec_as_e", "sn_a__k_ec_as_e"),
("SnAkEcAsE", "sn_ak_ec_as_e", "sn_ak_ec_as_e"),
("URLValue", "u_r_l_value", "url_value"),
("Xml2Json", "xml2_json", "xml2_json"),
("Xml2Json", "xml2_json", "xml2json"),
(" IPhone ", " _i_phone ", "i_phone"),
("I Phone", "i _phone", "i_phone"),
(" IPhone", " _i_phone", "i_phone"),
Expand Down

0 comments on commit 7c8a37a

Please sign in to comment.