Skip to content

Commit

Permalink
Merge pull request #3 from messerli-informatik-ag/split-on-numbers
Browse files Browse the repository at this point in the history
Split on numbers
  • Loading branch information
FreeApophis authored Sep 7, 2021
2 parents 9158f13 + 0d227ab commit 6dfc7d4
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 27 deletions.
10 changes: 10 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

All notable changes to this project will be documented in this file.
Messerli.ChangeCase adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Messerli.ChangeCase 1.1.0

* Support a single dot (`.`) as incoming separator.
* Numbers adjacent to words are parsed as words. (one or multiple digits)

4 changes: 4 additions & 0 deletions Messerli.ChangeCase.Test/FirstCharacterToLowerCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public class FirstCharacterToLowerCaseTest
[InlineData("this_is_a_long_snake_case_name", "this_is_a_long_snake_case_name")]
[InlineData("HTML", "hTML")]
[InlineData("htmlDocument", "htmlDocument")]
[InlineData("end_2_end", "end_2_end")]
[InlineData("BUSINESS_2_BUSINESS", "bUSINESS_2_BUSINESS")]
[InlineData("End2End", "end2End")]
[InlineData("business2Business", "business2Business")]
public void FirstCharacterOnlyGetsLowerCase(string sourceName, string expected)
{
Assert.Equal(expected, sourceName.FirstCharacterToLowerCase());
Expand Down
4 changes: 4 additions & 0 deletions Messerli.ChangeCase.Test/FirstCharacterToUpperCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public class FirstCharacterToUpperCaseTest
[InlineData("this_is_a_long_snake_case_name", "This_is_a_long_snake_case_name")]
[InlineData("HTML", "HTML")]
[InlineData("htmlDocument", "HtmlDocument")]
[InlineData("end_2_end", "End_2_end")]
[InlineData("BUSINESS_2_BUSINESS", "BUSINESS_2_BUSINESS")]
[InlineData("End2End", "End2End")]
[InlineData("business2Business", "Business2Business")]
public void FirstCharacterOnlyGetsUpperCase(string sourceName, string expected)
{
Assert.Equal(expected, sourceName.FirstCharacterToUpperCase());
Expand Down
5 changes: 5 additions & 0 deletions Messerli.ChangeCase.Test/ToCamelCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class ToCamelCaseTest
[InlineData("rmiSomething", "rmiSomething")]
[InlineData("RmiSomething", "rmiSomething")]
[InlineData("RMISomething", "rmiSomething")]
[InlineData("end_2_end", "end2End")]
[InlineData("BUSINESS_2_BUSINESS", "business2Business")]
[InlineData("End2End", "end2End")]
[InlineData("business2Business", "business2Business")]
[InlineData("e02", "e02")]
public void NamesAnyCasingConvertCorrectlyToCamelCase(string sourceName, string expected)
{
Assert.Equal(expected, sourceName.ToCamelCase());
Expand Down
5 changes: 5 additions & 0 deletions Messerli.ChangeCase.Test/ToKebabCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class ToKebabCaseTest
[InlineData("rmiSomething", "rmi-something")]
[InlineData("RmiSomething", "rmi-something")]
[InlineData("RMISomething", "rmi-something")]
[InlineData("end_2_end", "end-2-end")]
[InlineData("BUSINESS_2_BUSINESS", "business-2-business")]
[InlineData("End2End", "end-2-end")]
[InlineData("business2Business", "business-2-business")]
[InlineData("e02", "e-02")]
public void NamesAnyCasingConvertCorrectlyToKebabCase(string sourceName, string expected)
{
Assert.Equal(expected, sourceName.ToKebabCase());
Expand Down
5 changes: 5 additions & 0 deletions Messerli.ChangeCase.Test/ToPascalCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class ToPascalCaseTest
[InlineData("rmiSomething", "RmiSomething")]
[InlineData("RmiSomething", "RmiSomething")]
[InlineData("RMISomething", "RmiSomething")]
[InlineData("end_2_end", "End2End")]
[InlineData("BUSINESS_2_BUSINESS", "Business2Business")]
[InlineData("End2End", "End2End")]
[InlineData("business2Business", "Business2Business")]
[InlineData("e02", "E02")]
public void NamesAnyCasingConvertCorrectlyToPascalCase(string sourceName, string expected)
{
Assert.Equal(expected, sourceName.ToPascalCase());
Expand Down
5 changes: 5 additions & 0 deletions Messerli.ChangeCase.Test/ToSnakeCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class ToSnakeCaseTest
[InlineData("rmiSomething", "rmi_something")]
[InlineData("RmiSomething", "rmi_something")]
[InlineData("RMISomething", "rmi_something")]
[InlineData("end_2_end", "end_2_end")]
[InlineData("BUSINESS_2_BUSINESS", "business_2_business")]
[InlineData("End2End", "end_2_end")]
[InlineData("business2Business", "business_2_business")]
[InlineData("e02", "e_02")]
public void NamesAnyCasingConvertCorrectlyToSnakeCase(string sourceName, string expected)
{
Assert.Equal(expected, sourceName.ToSnakeCase());
Expand Down
5 changes: 5 additions & 0 deletions Messerli.ChangeCase.Test/ToUpperSnakeCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class ToUpperSnakeCaseTest
[InlineData("rmiSomething", "RMI_SOMETHING")]
[InlineData("RmiSomething", "RMI_SOMETHING")]
[InlineData("RMISomething", "RMI_SOMETHING")]
[InlineData("end_2_end", "END_2_END")]
[InlineData("BUSINESS_2_BUSINESS", "BUSINESS_2_BUSINESS")]
[InlineData("End2End", "END_2_END")]
[InlineData("business2Business", "BUSINESS_2_BUSINESS")]
[InlineData("e02", "E_02")]
public void NamesAnyCasingConvertCorrectlyToConstantCase(string sourceName, string expected)
{
Assert.Equal(expected, sourceName.ToUpperSnakeCase());
Expand Down
1 change: 1 addition & 0 deletions Messerli.ChangeCase.sln
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{50491EE5-F685-4F7B-ABC3-FAF5D7E46F99}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
ChangeLog.md = ChangeLog.md
Directory.Build.props = Directory.Build.props
global.json = global.json
Packages.props = Packages.props
Expand Down
2 changes: 1 addition & 1 deletion Messerli.ChangeCase/Messerli.ChangeCase.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Product>Messerli.ChangeCase</Product>
<Description>Transform a string between different casings.</Description>
<PackageTags>Utility Casing</PackageTags>
<Version>1.0.0</Version>
<Version>1.1.0</Version>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
Expand Down
54 changes: 39 additions & 15 deletions Messerli.ChangeCase/StringCaseExtensions.Private.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,42 @@ private static Option<SplitResult> SplitOnCasing(string identifier, int startInd
: ExtractByCasing(identifier, startIndex);

private static Option<SplitResult> ExtractByCasing(string identifier, int startIndex)
=> NextIsAbbreviation(identifier, startIndex)
? ExtractAbbreviation(identifier, startIndex)
: identifier
.WithIndex()
.Skip(startIndex + 1)
.FirstOrNone(c => char.IsUpper(c.Value))
.AndThen(c => c.Index)
.Match(
none: ExtractLastElement(identifier, startIndex),
some: ExtractNextElement(identifier, startIndex, EmptySeparatorLength));

private static Option<SplitResult> ExtractAbbreviation(string identifier, int startIndex)
=> identifier switch
{
_ when NextIsAbbreviation(identifier, startIndex) => ExtractUntil(identifier, startIndex, c => char.IsLower(c.Value)),
_ when NextIsNumber(identifier, startIndex) => ExtractUntil(identifier, startIndex, c => !char.IsDigit(c.Value)),
_ => ExtractNextWord(identifier, startIndex),
};

private static Option<SplitResult> ExtractUntil(string identifier, int startIndex, Func<ValueWithIndex<char>, bool> isEndPredicate)
=> identifier
.WithIndex()
.Skip(startIndex)
.FirstOrNone(c => char.IsLower(c.Value))
.AndThen(c => c.Index)
.FirstOrNone(isEndPredicate)
.AndThen(GetIndex)
.Match(
none: ExtractLastElement(identifier, startIndex),
some: index => ExtractNextElement(identifier, startIndex, EmptySeparatorLength)(index - 1));

private static bool NextIsNumber(string identifier, int startIndex)
=> identifier
.Skip(startIndex)
.TakeWhile(char.IsDigit)
.Count() > 1;

private static SplitResult ExtractNextWord(string identifier, int startIndex)
=> identifier
.WithIndex()
.Skip(startIndex + 1)
.FirstOrNone(IsSeparatorCase)
.AndThen(GetIndex)
.Match(
none: ExtractLastElement(identifier, startIndex),
some: ExtractNextElement(identifier, startIndex, EmptySeparatorLength));

private static bool IsSeparatorCase(ValueWithIndex<char> c)
=> char.IsUpper(c.Value) || char.IsDigit(c.Value);

private static bool NextIsAbbreviation(string identifier, int startIndex)
=> identifier
.Skip(startIndex)
Expand Down Expand Up @@ -90,7 +105,16 @@ private static string JoinStrings(this IEnumerable<string> strings, string separ

private static IEnumerable<string> SplitBy(this string text, ExtractElement extractNext)
=> Sequence
.Generate(new SplitResult(0), previous => extractNext(text, previous.NextStartIndex))
.Generate(new SplitResult(0, string.Empty), previous => extractNext(text, previous.NextStartIndex))
.Select(r => r.Result);

private static int GetIndex(ValueWithIndex<char> value)
=> value.Index;

private static string ToLower(string value)
=> value.ToLower();

private static string ToUpper(string value)
=> value.ToUpper();
}
}
10 changes: 2 additions & 8 deletions Messerli.ChangeCase/StringCaseExtensions.SplitResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,8 @@ private readonly struct SplitResult

public readonly int NextStartIndex;

public SplitResult(int nextStartIndex, Option<string> result = default)
{
Result = result
.Match(
none: string.Empty,
some: r => r.ToLower());
NextStartIndex = nextStartIndex;
}
public SplitResult(int nextStartIndex, string result)
=> (Result, NextStartIndex) = (result.ToLower(), nextStartIndex);
}
}
}
6 changes: 3 additions & 3 deletions Messerli.ChangeCase/StringCaseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ public static string ToCamelCase(this string identifier)

public static string ToUpperSnakeCase(this string identifier)
=> identifier
.FormatIdentifier(s => s.ToUpper(), "_");
.FormatIdentifier(ToUpper, "_");

public static string ToSnakeCase(this string identifier)
=> identifier
.FormatIdentifier(s => s.ToLower(), "_");
.FormatIdentifier(ToLower, "_");

public static string ToKebabCase(this string identifier)
=> identifier
.FormatIdentifier(s => s.ToLower(), "-");
.FormatIdentifier(ToLower, "-");

public static string FirstCharacterToUpperCase(this string name)
=> name.FirstOrNone()
Expand Down

0 comments on commit 6dfc7d4

Please sign in to comment.