From e3e52f25449af2e0e9368e464e7e9f5de94ecc6c Mon Sep 17 00:00:00 2001 From: David Kallesen Date: Thu, 25 Jan 2024 21:21:20 +0100 Subject: [PATCH 1/3] feat: Add string-extension EnsureEndsWithColon --- docs/CodeDoc/Atc/IndexExtended.md | 1 + docs/CodeDoc/Atc/System.md | 8 ++++++++ src/Atc/Extensions/StringExtensions.cs | 16 ++++++++++++++++ .../Extensions/StringExtensionsTests.cs | 8 ++++++++ 4 files changed, 33 insertions(+) diff --git a/docs/CodeDoc/Atc/IndexExtended.md b/docs/CodeDoc/Atc/IndexExtended.md index 117a5dcf..e95e3088 100644 --- a/docs/CodeDoc/Atc/IndexExtended.md +++ b/docs/CodeDoc/Atc/IndexExtended.md @@ -5119,6 +5119,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) diff --git a/docs/CodeDoc/Atc/System.md b/docs/CodeDoc/Atc/System.md index 9b754460..c50ade2c 100644 --- a/docs/CodeDoc/Atc/System.md +++ b/docs/CodeDoc/Atc/System.md @@ -1872,6 +1872,14 @@ Extensions for the string class. >     `appendValue`  -  The append value.
> >Returns: The string that is cutoff by the max-length and appended with the appendValue. +#### EnsureEndsWithColon +>```csharp +>string EnsureEndsWithColon(this string value) +>``` +>Summary: Ensures the string-value ends with a ':'. +> +>Parameters:
+>     `value`  -  The value.
#### EnsureEndsWithDot >```csharp >string EnsureEndsWithDot(this string value) diff --git a/src/Atc/Extensions/StringExtensions.cs b/src/Atc/Extensions/StringExtensions.cs index 7dbaeb77..3a641b96 100644 --- a/src/Atc/Extensions/StringExtensions.cs +++ b/src/Atc/Extensions/StringExtensions.cs @@ -1209,6 +1209,22 @@ public static string EnsureEndsWithDot(this string value) : $"{value}."; } + /// + /// Ensures the string-value ends with a ':'. + /// + /// The value. + public static string EnsureEndsWithColon(this string value) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + return value.EndsWith(':') + ? value + : $"{value}:"; + } + /// /// Ensures the singular. /// diff --git a/test/Atc.Tests/Extensions/StringExtensionsTests.cs b/test/Atc.Tests/Extensions/StringExtensionsTests.cs index 4aded334..953bef06 100644 --- a/test/Atc.Tests/Extensions/StringExtensionsTests.cs +++ b/test/Atc.Tests/Extensions/StringExtensionsTests.cs @@ -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")] From 125ad943e18bfbd85e11b6e50aff3757c45c0699 Mon Sep 17 00:00:00 2001 From: David Kallesen Date: Thu, 25 Jan 2024 21:23:15 +0100 Subject: [PATCH 2/3] feat: Add StringCaseFormatter --- docs/CodeDoc/Atc/Atc.md | 27 +++++ docs/CodeDoc/Atc/Index.md | 1 + docs/CodeDoc/Atc/IndexExtended.md | 6 + src/Atc/Formatters/StringCaseFormatter.cs | 92 +++++++++++++++ .../Formatters/StringCaseFormatterTests.cs | 110 ++++++++++++++++++ 5 files changed, 236 insertions(+) create mode 100644 src/Atc/Formatters/StringCaseFormatter.cs create mode 100644 test/Atc.Tests/Formatters/StringCaseFormatterTests.cs diff --git a/docs/CodeDoc/Atc/Atc.md b/docs/CodeDoc/Atc/Atc.md index 10f3fa63..77376c07 100644 --- a/docs/CodeDoc/Atc/Atc.md +++ b/docs/CodeDoc/Atc/Atc.md @@ -1144,6 +1144,33 @@ Enumeration: SortDirectionType. +
+ +## StringCaseFormatter +Provides custom string formatting based on specified case formatting options.

Supported formats: UConverts the entire string to uppercase.uCapitalizes the first character of the string.LConverts the entire string to lowercase.lConverts the first character of the string to lowercase.UlConverts the string to uppercase with the first character in lowercase.LuConverts the string to lowercase with the first character capitalized.[Format]:Applies the specified format and appends a colon ´:´ to the end.[Format].Applies the specified format and appends a period ´.´ to the end. Examples: string.Format(StringCaseFormatter.Default, "{0:U} {1:u} {2:L} {3:l}", .... 4 parameters );string.Format(StringCaseFormatter.Default, "{0:U.} {1:u.} {2:L:} {3:l:}", .... 4 parameters );string.Format(StringCaseFormatter.Default, "{0:Ul:} {1:Lu:}", ... 2 parameters );string.Format(new StringCaseFormatter(), "{0:U} {1:u} {2:L} {3:l}", ... 4 parameters ); + +>```csharp +>public class StringCaseFormatter : IFormatProvider, ICustomFormatter +>``` + +### Static Fields + +#### Default +>```csharp +>StringCaseFormatter Default +>``` +>Summary: 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) +>``` +
## TemplatePatternType diff --git a/docs/CodeDoc/Atc/Index.md b/docs/CodeDoc/Atc/Index.md index f53eaf21..5fd05d04 100644 --- a/docs/CodeDoc/Atc/Index.md +++ b/docs/CodeDoc/Atc/Index.md @@ -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<T1, T2>](Atc.md#tupleequalitycomparer<t1-t2>) diff --git a/docs/CodeDoc/Atc/IndexExtended.md b/docs/CodeDoc/Atc/IndexExtended.md index e95e3088..841a1910 100644 --- a/docs/CodeDoc/Atc/IndexExtended.md +++ b/docs/CodeDoc/Atc/IndexExtended.md @@ -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<T1, T2>](Atc.md#tupleequalitycomparer<t1-t2>) diff --git a/src/Atc/Formatters/StringCaseFormatter.cs b/src/Atc/Formatters/StringCaseFormatter.cs new file mode 100644 index 00000000..03a62861 --- /dev/null +++ b/src/Atc/Formatters/StringCaseFormatter.cs @@ -0,0 +1,92 @@ +// ReSharper disable once CheckNamespace +namespace Atc; + +/// +/// Provides custom string formatting based on specified case formatting options.

+/// Supported formats: +/// +/// +/// U +/// Converts the entire string to uppercase. +/// +/// +/// u +/// Capitalizes the first character of the string. +/// +/// +/// L +/// Converts the entire string to lowercase. +/// +/// +/// l +/// Converts the first character of the string to lowercase. +/// +/// +/// Ul +/// Converts the string to uppercase with the first character in lowercase. +/// +/// +/// Lu +/// Converts the string to lowercase with the first character capitalized. +/// +/// +/// [Format]: +/// Applies the specified format and appends a colon ´:´ to the end. +/// +/// +/// [Format]. +/// Applies the specified format and appends a period ´.´ to the end. +/// +/// +/// Examples: +/// string.Format(StringCaseFormatter.Default, "{0:U} {1:u} {2:L} {3:l}", .... 4 parameters ); +/// string.Format(StringCaseFormatter.Default, "{0:U.} {1:u.} {2:L:} {3:l:}", .... 4 parameters ); +/// string.Format(StringCaseFormatter.Default, "{0:Ul:} {1:Lu:}", ... 2 parameters ); +/// string.Format(new StringCaseFormatter(), "{0:U} {1:u} {2:L} {3:l}", ... 4 parameters ); +///
+public sealed class StringCaseFormatter : IFormatProvider, ICustomFormatter +{ + /// + /// Static using . + /// + 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, + }; + } +} \ No newline at end of file diff --git a/test/Atc.Tests/Formatters/StringCaseFormatterTests.cs b/test/Atc.Tests/Formatters/StringCaseFormatterTests.cs new file mode 100644 index 00000000..d717e4d4 --- /dev/null +++ b/test/Atc.Tests/Formatters/StringCaseFormatterTests.cs @@ -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)); +} \ No newline at end of file From c839e426748871852938ec511ba1598c1256fb30 Mon Sep 17 00:00:00 2001 From: David Kallesen Date: Thu, 25 Jan 2024 21:29:35 +0100 Subject: [PATCH 3/3] chore: UT are made for StringCaseFormatter, but due to bug in CodeComplianceTestHelper, this is suppressed --- test/Atc.Tests/CodeComplianceTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Atc.Tests/CodeComplianceTests.cs b/test/Atc.Tests/CodeComplianceTests.cs index 0c935523..76341207 100644 --- a/test/Atc.Tests/CodeComplianceTests.cs +++ b/test/Atc.Tests/CodeComplianceTests.cs @@ -44,6 +44,7 @@ public class CodeComplianceTests typeof(EnumAtcExtensions), typeof(DynamicJson), typeof(EnumHelper), + typeof(StringCaseFormatter), typeof(NumberHelper), typeof(InternetBrowserHelper), typeof(FileInfoExtensions),