Skip to content

Commit

Permalink
Merge pull request #304 from atc-net/feature/StringCaseFormatter
Browse files Browse the repository at this point in the history
Add StringCaseFormatter
  • Loading branch information
davidkallesen authored Jan 25, 2024
2 parents 5adda98 + c839e42 commit 4bec200
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/CodeDoc/Atc/Atc.md
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,33 @@ Enumeration: SortDirectionType.
<br />
## StringCaseFormatter
Provides custom string formatting based on specified case formatting options.<br /><br /> Supported formats: <list type="table"><item><term>U</term><description>Converts the entire string to uppercase.</description></item><item><term>u</term><description>Capitalizes the first character of the string.</description></item><item><term>L</term><description>Converts the entire string to lowercase.</description></item><item><term>l</term><description>Converts the first character of the string to lowercase.</description></item><item><term>Ul</term><description>Converts the string to uppercase with the first character in lowercase.</description></item><item><term>Lu</term><description>Converts the string to lowercase with the first character capitalized.</description></item><item><term>[Format]:</term><description>Applies the specified format and appends a colon ´<b>:</b>´ to the end.</description></item><item><term>[Format].</term><description>Applies the specified format and appends a period ´<b>.</b>´ to the end.</description></item></list> Examples: <code>string.Format(StringCaseFormatter.Default, "{0:U} {1:u} {2:L} {3:l}", .... 4 parameters );</code><code>string.Format(StringCaseFormatter.Default, "{0:U.} {1:u.} {2:L:} {3:l:}", .... 4 parameters );</code><code>string.Format(StringCaseFormatter.Default, "{0:Ul:} {1:Lu:}", ... 2 parameters );</code><code>string.Format(new StringCaseFormatter(), "{0:U} {1:u} {2:L} {3:l}", ... 4 parameters );</code>
>```csharp
>public class StringCaseFormatter : IFormatProvider, ICustomFormatter
>```
### Static Fields
#### Default
>```csharp
>StringCaseFormatter Default
>```
><b>Summary:</b> Static `Atc.StringCaseFormatter` using `System.Globalization.CultureInfo.CurrentCulture`.
### Methods
#### Format
>```csharp
>string Format(string format, object arg, IFormatProvider formatProvider)
>```
#### GetFormat
>```csharp
>object GetFormat(Type formatType)
>```
<br />
## TemplatePatternType
Expand Down
1 change: 1 addition & 0 deletions docs/CodeDoc/Atc/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
- [Point2D](Atc.md#point2d)
- [Point3D](Atc.md#point3d)
- [SortDirectionType](Atc.md#sortdirectiontype)
- [StringCaseFormatter](Atc.md#stringcaseformatter)
- [TemplatePatternType](Atc.md#templatepatterntype)
- [TriggerActionType](Atc.md#triggeractiontype)
- [TupleEqualityComparer&lt;T1, T2&gt;](Atc.md#tupleequalitycomparer&lt;t1-t2&gt;)
Expand Down
7 changes: 7 additions & 0 deletions docs/CodeDoc/Atc/IndexExtended.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@
- ToString()
- ToStringShort()
- [SortDirectionType](Atc.md#sortdirectiontype)
- [StringCaseFormatter](Atc.md#stringcaseformatter)
- Static Fields
- StringCaseFormatter Default
- Methods
- Format(string format, object arg, IFormatProvider formatProvider)
- GetFormat(Type formatType)
- [TemplatePatternType](Atc.md#templatepatterntype)
- [TriggerActionType](Atc.md#triggeractiontype)
- [TupleEqualityComparer&lt;T1, T2&gt;](Atc.md#tupleequalitycomparer&lt;t1-t2&gt;)
Expand Down Expand Up @@ -5119,6 +5125,7 @@
- Contains(this string value, string[] containsValues, bool ignoreCaseSensitive = True)
- ContainsTemplatePattern(this string value, TemplatePatternType templatePatternType = HardBrackets)
- Cut(this string value, int maxLength, string appendValue = ...)
- EnsureEndsWithColon(this string value)
- EnsureEndsWithDot(this string value)
- EnsureEnvironmentNewLines(this string value)
- EnsureFirstCharacterToLower(this string value)
Expand Down
8 changes: 8 additions & 0 deletions docs/CodeDoc/Atc/System.md
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,14 @@ Extensions for the string class.
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`appendValue`&nbsp;&nbsp;-&nbsp;&nbsp;The append value.<br />
>
><b>Returns:</b> The string that is cutoff by the max-length and appended with the appendValue.
#### EnsureEndsWithColon
>```csharp
>string EnsureEndsWithColon(this string value)
>```
><b>Summary:</b> Ensures the string-value ends with a ':'.
>
><b>Parameters:</b><br>
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`value`&nbsp;&nbsp;-&nbsp;&nbsp;The value.<br />
#### EnsureEndsWithDot
>```csharp
>string EnsureEndsWithDot(this string value)
Expand Down
16 changes: 16 additions & 0 deletions src/Atc/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,22 @@ public static string EnsureEndsWithDot(this string value)
: $"{value}.";
}

/// <summary>
/// Ensures the string-value ends with a ':'.
/// </summary>
/// <param name="value">The value.</param>
public static string EnsureEndsWithColon(this string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

return value.EndsWith(':')
? value
: $"{value}:";
}

/// <summary>
/// Ensures the singular.
/// </summary>
Expand Down
92 changes: 92 additions & 0 deletions src/Atc/Formatters/StringCaseFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// ReSharper disable once CheckNamespace
namespace Atc;

/// <summary>
/// Provides custom string formatting based on specified case formatting options.<br /><br />
/// Supported formats:
/// <list type="table">
/// <item>
/// <term>U</term>
/// <description>Converts the entire string to uppercase.</description>
/// </item>
/// <item>
/// <term>u</term>
/// <description>Capitalizes the first character of the string.</description>
/// </item>
/// <item>
/// <term>L</term>
/// <description>Converts the entire string to lowercase.</description>
/// </item>
/// <item>
/// <term>l</term>
/// <description>Converts the first character of the string to lowercase.</description>
/// </item>
/// <item>
/// <term>Ul</term>
/// <description>Converts the string to uppercase with the first character in lowercase.</description>
/// </item>
/// <item>
/// <term>Lu</term>
/// <description>Converts the string to lowercase with the first character capitalized.</description>
/// </item>
/// <item>
/// <term>[Format]:</term>
/// <description>Applies the specified format and appends a colon ´<b>:</b>´ to the end.</description>
/// </item>
/// <item>
/// <term>[Format].</term>
/// <description>Applies the specified format and appends a period ´<b>.</b>´ to the end.</description>
/// </item>
/// </list>
/// Examples:
/// <code>string.Format(StringCaseFormatter.Default, "{0:U} {1:u} {2:L} {3:l}", .... 4 parameters );</code>
/// <code>string.Format(StringCaseFormatter.Default, "{0:U.} {1:u.} {2:L:} {3:l:}", .... 4 parameters );</code>
/// <code>string.Format(StringCaseFormatter.Default, "{0:Ul:} {1:Lu:}", ... 2 parameters );</code>
/// <code>string.Format(new StringCaseFormatter(), "{0:U} {1:u} {2:L} {3:l}", ... 4 parameters );</code>
/// </summary>
public sealed class StringCaseFormatter : IFormatProvider, ICustomFormatter
{
/// <summary>
/// Static <see cref="StringCaseFormatter"/> using <see cref="CultureInfo.CurrentCulture"/>.
/// </summary>
public static readonly StringCaseFormatter Default = new();

public object? GetFormat(Type? formatType)
=> formatType == typeof(ICustomFormatter)
? this
: null;

public string Format(
string? format,
object? arg,
IFormatProvider? formatProvider)
{
var str = arg?.ToString() ?? string.Empty;

return format switch
{
"U" => str.ToUpper(CultureInfo.CurrentCulture),
"Ul" => str.ToUpper(CultureInfo.CurrentCulture).EnsureFirstCharacterToLower(),
"u" => str.EnsureFirstCharacterToUpper(),
"L" => str.ToLower(CultureInfo.CurrentCulture),
"Lu" => str.ToLower(CultureInfo.CurrentCulture).EnsureFirstCharacterToUpper(),
"l" => str.EnsureFirstCharacterToLower(),

"U." => str.ToUpper(CultureInfo.CurrentCulture).EnsureEndsWithDot(),
"Ul." => str.ToUpper(CultureInfo.CurrentCulture).EnsureFirstCharacterToLower().EnsureEndsWithDot(),
"u." => str.EnsureFirstCharacterToUpper().EnsureEndsWithDot(),
"L." => str.ToLower(CultureInfo.CurrentCulture).EnsureEndsWithDot(),
"Lu." => str.ToLower(CultureInfo.CurrentCulture).EnsureFirstCharacterToUpper().EnsureEndsWithDot(),
"l." => str.EnsureFirstCharacterToLower().EnsureEndsWithDot(),

"U:" => str.ToUpper(CultureInfo.CurrentCulture).EnsureEndsWithColon(),
"Ul:" => str.ToUpper(CultureInfo.CurrentCulture).EnsureFirstCharacterToLower().EnsureEndsWithColon(),
"u:" => str.EnsureFirstCharacterToUpper().EnsureEndsWithColon(),
"L:" => str.ToLower(CultureInfo.CurrentCulture).EnsureEndsWithColon(),
"Lu:" => str.ToLower(CultureInfo.CurrentCulture).EnsureFirstCharacterToUpper().EnsureEndsWithColon(),
"l:" => str.EnsureFirstCharacterToLower().EnsureEndsWithColon(),

_ => str,
};
}
}
1 change: 1 addition & 0 deletions test/Atc.Tests/CodeComplianceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class CodeComplianceTests
typeof(EnumAtcExtensions),
typeof(DynamicJson),
typeof(EnumHelper),
typeof(StringCaseFormatter),
typeof(NumberHelper),
typeof(InternetBrowserHelper),
typeof(FileInfoExtensions),
Expand Down
8 changes: 8 additions & 0 deletions test/Atc.Tests/Extensions/StringExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,18 @@ public void EnsureFirstCharacterToLower(string expected, string input)

[Theory]
[InlineData("hallo.", "hallo")]
[InlineData("hallo:.", "hallo:")]
[InlineData("hallo.", "hallo.")]
public void EnsureEndsWithDot(string expected, string input)
=> Assert.Equal(expected, input.EnsureEndsWithDot());

[Theory]
[InlineData("hallo:", "hallo")]
[InlineData("hallo.:", "hallo.")]
[InlineData("hallo:", "hallo:")]
public void EnsureEndsWithColon(string expected, string input)
=> Assert.Equal(expected, input.EnsureEndsWithColon());

[Theory]
[InlineData("Hallo", "Hallo")]
[InlineData("Hallo", "Hallos")]
Expand Down
110 changes: 110 additions & 0 deletions test/Atc.Tests/Formatters/StringCaseFormatterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// ReSharper disable StringLiteralTypo
#pragma warning disable xUnit1012
namespace Atc.Tests.Formatters;

public class StringCaseFormatterTests
{
private readonly StringCaseFormatter formatter = StringCaseFormatter.Default;

[Theory]
[InlineData("test", "TesT", "L")]
[InlineData("tesT", "TesT", "l")]
[InlineData("TEST", "TesT", "U")]
[InlineData("TesT", "TesT", "u")]
[InlineData("test", "tESt", "L")]
[InlineData("tESt", "tESt", "l")]
[InlineData("TEST", "tESt", "U")]
[InlineData("TESt", "tESt", "u")]
[InlineData("test.", "TesT", "L.")]
[InlineData("tesT.", "TesT", "l.")]
[InlineData("TEST.", "TesT", "U.")]
[InlineData("TesT.", "TesT", "u.")]
[InlineData("test.", "tESt", "L.")]
[InlineData("tESt.", "tESt", "l.")]
[InlineData("TEST.", "tESt", "U.")]
[InlineData("TESt.", "tESt", "u.")]
[InlineData("test:", "TesT", "L:")]
[InlineData("tesT:", "TesT", "l:")]
[InlineData("TEST:", "TesT", "U:")]
[InlineData("TesT:", "TesT", "u:")]
[InlineData("test:", "tESt", "L:")]
[InlineData("tESt:", "tESt", "l:")]
[InlineData("TEST:", "tESt", "U:")]
[InlineData("TESt:", "tESt", "u:")]
public void FormatStringForDefault(
string expected,
string input,
string formatSpecifier)
=> Assert.Equal(
expected,
string.Format(StringCaseFormatter.Default, $"{{0:{formatSpecifier}}}", input));

[Theory]
[InlineData("test", "test", "L")]
[InlineData("test", "TEST", "L")]
[InlineData("test", "test", "l")]
[InlineData("tEST", "TEST", "l")]
[InlineData("TEST", "test", "U")]
[InlineData("TEST", "TEST", "U")]
[InlineData("Test", "test", "u")]
[InlineData("TEST", "TEST", "u")]
[InlineData("test", "test", "x")]
[InlineData("test", "test", "")]
[InlineData("", null, "U")]
[InlineData("john doe", "john doe", "L")]
[InlineData("john doe", "john doe", "l")]
[InlineData("JOHN DOE", "john doe", "U")]
[InlineData("John doe", "john doe", "u")]
[InlineData("john doe", "JOHN DOE", "L")]
[InlineData("jOHN DOE", "JOHN DOE", "l")]
[InlineData("JOHN DOE", "JOHN DOE", "U")]
[InlineData("JOHN DOE", "JOHN DOE", "u")]
[InlineData("John doe", "JOHN DOE", "Lu")]
[InlineData("jOHN DOE", "JOHN DOE", "Ul")]
[InlineData("John doe.", "john doe", "Lu.")]
[InlineData("jOHN DOE.", "JOHN DOE", "Ul.")]
[InlineData("John doe:", "john doe", "Lu:")]
[InlineData("jOHN DOE:", "john doe", "Ul:")]
public void FormatStringForOneParameter(
string expected,
string input,
string formatSpecifier)
=> Assert.Equal(
expected,
string.Format(formatter, $"{{0:{formatSpecifier}}}", input));

[Theory]
[InlineData("hallo world", "Hallo", "World", "L", "L")]
[InlineData("hallo world", "Hallo", "World", "L", "l")]
[InlineData("hallo world", "Hallo", "World", "l", "l")]
[InlineData("hallo world", "Hallo", "World", "l", "L")]
[InlineData("hallo world", "HALLO", "WORLD", "L", "L")]
[InlineData("hallo wORLD", "HALLO", "WORLD", "L", "l")]
[InlineData("hALLO wORLD", "HALLO", "WORLD", "l", "l")]
[InlineData("hALLO world", "HALLO", "WORLD", "l", "L")]
[InlineData("hallo world", "hallo", "world", "L", "L")]
[InlineData("hallo world", "hallo", "world", "L", "l")]
[InlineData("hallo world", "hallo", "world", "l", "l")]
[InlineData("hallo world", "hallo", "world", "l", "L")]
[InlineData("HALLO WORLD", "Hallo", "World", "U", "U")]
[InlineData("HALLO World", "Hallo", "World", "U", "u")]
[InlineData("Hallo World", "Hallo", "World", "u", "u")]
[InlineData("Hallo WORLD", "Hallo", "World", "u", "U")]
[InlineData("HALLO WORLD", "HALLO", "WORLD", "U", "U")]
[InlineData("HALLO WORLD", "HALLO", "WORLD", "U", "u")]
[InlineData("HALLO WORLD", "HALLO", "WORLD", "u", "u")]
[InlineData("HALLO WORLD", "HALLO", "WORLD", "u", "U")]
[InlineData("HALLO WORLD", "hallo", "world", "U", "U")]
[InlineData("HALLO World", "hallo", "world", "U", "u")]
[InlineData("Hallo World", "hallo", "world", "u", "u")]
[InlineData("Hallo WORLD", "hallo", "world", "u", "U")]
public void FormatStringForTwoParameters(
string expected,
string input1,
string input2,
string formatSpecifier1,
string formatSpecifier2)
=> Assert.Equal(
expected,
string.Format(formatter, $"{{0:{formatSpecifier1}}} {{1:{formatSpecifier2}}}", input1, input2));
}

0 comments on commit 4bec200

Please sign in to comment.