From 30e925cdb076f24ba358a629e4d607efda7a56af Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Sun, 24 Apr 2022 10:09:56 +0100 Subject: [PATCH] Move additional methods into child namespaces (#7) --- X10D.Tests/src/Collections/ArrayTests.cs | 106 +++++++ X10D.Tests/src/Core/ArrayTests.cs | 60 ---- X10D.Tests/src/Core/CoreTests.cs | 1 - X10D.Tests/src/Core/StringTests.cs | 180 +----------- X10D.Tests/src/Text/RuneTests.cs | 6 +- X10D.Tests/src/Text/StringTests.cs | 208 +++++++++++++- .../ArrayExtensions.cs | 2 +- X10D/src/StringExtensions/StringExtensions.cs | 269 ------------------ X10D/src/Text/CharExtensions.cs | 23 +- X10D/src/Text/RuneExtensions.cs | 21 +- X10D/src/Text/StringExtensions.cs | 266 ++++++++++++++++- 11 files changed, 596 insertions(+), 546 deletions(-) create mode 100644 X10D.Tests/src/Collections/ArrayTests.cs delete mode 100644 X10D.Tests/src/Core/ArrayTests.cs rename X10D/src/{ArrayExtensions => Collections}/ArrayExtensions.cs (99%) diff --git a/X10D.Tests/src/Collections/ArrayTests.cs b/X10D.Tests/src/Collections/ArrayTests.cs new file mode 100644 index 0000000..c72d5ec --- /dev/null +++ b/X10D.Tests/src/Collections/ArrayTests.cs @@ -0,0 +1,106 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using X10D.Collections; +using X10D.Core; + +namespace X10D.Tests.Collections; + +[TestClass] +public class ArrayTests +{ + [TestMethod] + [DataRow(1)] + [DataRow("f")] + [DataRow(true)] + public void AsArrayShouldGiveLength1(object o) + { + object[] array = o.AsArray()!; + Assert.IsNotNull(array); + Assert.IsTrue(array.Length == 1); + } + + [TestMethod] + [DataRow(1)] + [DataRow("f")] + [DataRow(true)] + public void AsArrayShouldContainObject(object o) + { + object[] array = o.AsArray()!; + Assert.IsNotNull(array); + Assert.AreEqual(o, array[0]); + } + + [TestMethod] + public void AsReadOnlyShouldBeReadOnly() + { + var array = new object[] {1, "f", true}; + var readOnly = array.AsReadOnly(); + Assert.IsNotNull(readOnly); + Assert.IsTrue(readOnly.Count == 3); + + // ReSharper disable once ConvertTypeCheckToNullCheck + Assert.IsTrue(readOnly is IReadOnlyCollection); + } + + [TestMethod] + public void AsReadOnlyNullShouldThrow() + { + object[]? array = null; + Assert.ThrowsException(array!.AsReadOnly); + } + + [TestMethod] + [DataRow] + [DataRow(1)] + [DataRow(1, 2, 3)] + [DataRow(1, 2, 3, 4, 5)] + public void ClearShouldFillDefault(params int[] args) + { + args.Clear(); + + int[] clearedArray = Enumerable.Repeat(0, args.Length).ToArray(); + CollectionAssert.AreEqual(clearedArray, args); + } + + [TestMethod] + public void ClearNullShouldThrow() + { + int[]? array = null; + Assert.ThrowsException(array!.Clear); + Assert.ThrowsException(() => array!.Clear(0, 0)); + } + + [TestMethod] + [DataRow] + [DataRow(1)] + [DataRow(1, 2, 3)] + [DataRow(1, 2, 3, 4, 5)] + public void FillShouldBeCorrect(params int[] args) + { + args.Fill(1); + + int[] comparison = Enumerable.Repeat(1, args.Length).ToArray(); + CollectionAssert.AreEqual(comparison, args); + } + + [TestMethod] + public void FillNullShouldThrow() + { + int[]? array = null; + Assert.ThrowsException(() => array!.Fill(0)); + Assert.ThrowsException(() => array!.Fill(0, 0, 0)); + } + + [TestMethod] + [DataRow(1)] + [DataRow(1, 2, 3)] + [DataRow(1, 2, 3, 4, 5)] + public void FillSlicedShouldBeCorrect(params int[] args) + { + int first = args[0]; + args.Fill(1, 1, args.Length - 1); + + int[] comparison = Enumerable.Repeat(1, args.Length - 1).ToArray(); + Assert.AreEqual(first, args[0]); + CollectionAssert.AreEqual(comparison, args[1..]); + } +} diff --git a/X10D.Tests/src/Core/ArrayTests.cs b/X10D.Tests/src/Core/ArrayTests.cs deleted file mode 100644 index 5549a4d..0000000 --- a/X10D.Tests/src/Core/ArrayTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using X10D.Core; - -namespace X10D.Tests.Core; - -[TestClass] -public class ArrayTests -{ - [TestMethod] - [DataRow(1)] - [DataRow("f")] - [DataRow(true)] - public void AsArray(object o) - { - object[] array = o.AsArray(); - Assert.IsNotNull(array); - Assert.IsTrue(array.Length == 1); - Assert.AreEqual(o, array[0]); - } - - [TestMethod] - [DataRow] - [DataRow(1)] - [DataRow(1, 2, 3)] - [DataRow(1, 2, 3, 4, 5)] - public void Clear(params int[] args) - { - args.Clear(); - - int[] comparison = Enumerable.Repeat(0, args.Length).ToArray(); - CollectionAssert.AreEqual(args, comparison); - } - - [TestMethod] - public void ClearNull() - { - int[]? array = null; - Assert.ThrowsException(array!.Clear); - } - - [TestMethod] - [DataRow] - [DataRow(1)] - [DataRow(1, 2, 3)] - [DataRow(1, 2, 3, 4, 5)] - public void Fill(params int[] args) - { - args.Fill(1); - - int[] comparison = Enumerable.Repeat(1, args.Length).ToArray(); - CollectionAssert.AreEqual(args, comparison); - } - - [TestMethod] - public void FillNull() - { - int[]? array = null; - Assert.ThrowsException(() => array!.Fill(0)); - } -} diff --git a/X10D.Tests/src/Core/CoreTests.cs b/X10D.Tests/src/Core/CoreTests.cs index 1ce041e..35e9f53 100644 --- a/X10D.Tests/src/Core/CoreTests.cs +++ b/X10D.Tests/src/Core/CoreTests.cs @@ -1,6 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using X10D.Core; -using X10D.Text; namespace X10D.Tests.Core; diff --git a/X10D.Tests/src/Core/StringTests.cs b/X10D.Tests/src/Core/StringTests.cs index c0e76f8..ee6d169 100644 --- a/X10D.Tests/src/Core/StringTests.cs +++ b/X10D.Tests/src/Core/StringTests.cs @@ -8,50 +8,6 @@ namespace X10D.Tests.Core; [TestClass] public class StringTests { - /// - /// Tests . - /// - [TestMethod] - public void AsNullIfEmpty() - { - const string sampleString = "Hello World"; - const string whitespaceString = " "; - const string emptyString = ""; - const string? nullString = null; - - string sampleResult = sampleString.AsNullIfEmpty(); - string whitespaceResult = whitespaceString.AsNullIfEmpty(); - string emptyResult = emptyString.AsNullIfEmpty(); - string? nullResult = nullString.AsNullIfEmpty(); - - Assert.AreEqual(sampleString, sampleResult); - Assert.AreEqual(whitespaceString, whitespaceResult); - Assert.AreEqual(nullString, emptyResult); - Assert.AreEqual(nullString, nullResult); - } - - /// - /// Tests . - /// - [TestMethod] - public void AsNullIfWhiteSpace() - { - const string sampleString = "Hello World"; - const string whitespaceString = " "; - const string emptyString = ""; - const string? nullString = null; - - string sampleResult = sampleString.AsNullIfWhiteSpace(); - string whitespaceResult = whitespaceString.AsNullIfWhiteSpace(); - string emptyResult = emptyString.AsNullIfWhiteSpace(); - string? nullResult = nullString.AsNullIfWhiteSpace(); - - Assert.AreEqual(sampleString, sampleResult); - Assert.AreEqual(nullString, whitespaceResult); - Assert.AreEqual(nullString, emptyResult); - Assert.AreEqual(nullString, nullResult); - } - /// /// Tests . /// @@ -81,78 +37,6 @@ public class StringTests Assert.AreEqual(expected, result); } - /// - /// Tests . - /// - [TestMethod] - public void IsLower() - { - const string inputA = "Hello World"; - const string inputB = "hello world"; - const string? nullString = null; - - bool resultA = inputA.IsLower(); - bool resultB = inputB.IsLower(); - - Assert.ThrowsException(() => nullString!.IsLower()); - Assert.IsFalse(resultA); - Assert.IsTrue(resultB); - } - - [TestMethod] - public void IsPalindrome() - { - const string inputA = "Race car"; - const string inputB = "Racecar"; - const string inputC = "A man, a plan, a canal, panama"; - const string inputD = "Jackdaws love my big sphinx of quartz"; - const string inputE = "Y"; - const string inputF = "1"; - const string inputG = ""; - - Assert.IsTrue(inputA.IsPalindrome(), inputA); - Assert.IsTrue(inputB.IsPalindrome(), inputB); - Assert.IsTrue(inputC.IsPalindrome(), inputC); - Assert.IsFalse(inputD.IsPalindrome(), inputD); - Assert.IsTrue(inputE.IsPalindrome(), inputE); - Assert.IsTrue(inputF.IsPalindrome(), inputF); - Assert.IsFalse(inputG.IsPalindrome(), inputG); - } - - /// - /// Tests . - /// - [TestMethod] - public void IsUpper() - { - const string inputA = "Hello World"; - const string inputB = "HELLO WORLD"; - const string? nullString = null; - - bool resultA = inputA.IsUpper(); - bool resultB = inputB.IsUpper(); - - Assert.ThrowsException(() => nullString!.IsUpper()); - Assert.IsFalse(resultA); - Assert.IsTrue(resultB); - } - - /// - /// Tests . - /// - [TestMethod] - public void Shuffled() - { - const string input = "Hello World"; - const string expected = " oHlldrWoel"; - var random = new Random(1); - - string result = input.Shuffled(random); - - Assert.ThrowsException(() => ((string?)null)!.Shuffled()); - Assert.AreEqual(expected, result); - } - /// /// Tests . /// @@ -170,66 +54,4 @@ public class StringTests Assert.AreEqual(string.Empty, string.Empty.Randomize(0)); Assert.AreEqual(expected, result); } - - /// - /// Tests . - /// - [TestMethod] - public void Repeat() - { - const string input = "Hello World"; - const string expected = "Hello WorldHello WorldHello WorldHello WorldHello WorldHello WorldHello WorldHello World"; - const int repeatCount = 8; - - string result = input.Repeat(repeatCount); - - Assert.ThrowsException(() => ((string?)null)!.Repeat(repeatCount)); - Assert.ThrowsException(() => input.Repeat(-1)); - Assert.AreEqual(expected, result); - } - - /// - /// Tests . - /// - [TestMethod] - public void Reverse() - { - const string input = "Hello World"; - const string expected = "dlroW olleH"; - - string result = input.Reverse(); - - Assert.ThrowsException(() => ((string?)null)!.Reverse()); - Assert.AreEqual(string.Empty.Reverse(), string.Empty); - Assert.AreEqual(" ".Reverse(), " "); - Assert.AreEqual(expected, result); - } - - /// - /// Tests and - /// . - /// - [TestMethod] - public void WithAlternative() - { - const string inputA = "Hello World"; - const string inputB = " "; - const string inputC = ""; - const string? inputD = null; - const string alternative = "ALTERNATIVE"; - - string resultA = inputA.WithEmptyAlternative(alternative); - string resultB = inputB.WithEmptyAlternative(alternative); - string resultBWithWhitespace = inputB.WithWhiteSpaceAlternative(alternative); - string resultC = inputC.WithEmptyAlternative(alternative); - string resultD = inputD.WithEmptyAlternative(alternative); - - Assert.AreEqual(resultA, inputA); - Assert.AreEqual(resultB, inputB); - Assert.AreEqual(resultBWithWhitespace, alternative); - Assert.AreEqual(resultC, alternative); - Assert.AreEqual(resultD, alternative); - Assert.AreEqual(alternative, ((string?)null).WithEmptyAlternative(alternative)); - Assert.AreEqual(alternative, ((string?)null).WithWhiteSpaceAlternative(alternative)); - } -} \ No newline at end of file +} diff --git a/X10D.Tests/src/Text/RuneTests.cs b/X10D.Tests/src/Text/RuneTests.cs index e85a8a5..7d3315d 100644 --- a/X10D.Tests/src/Text/RuneTests.cs +++ b/X10D.Tests/src/Text/RuneTests.cs @@ -16,7 +16,7 @@ public class RuneTests Assert.AreEqual(expected, actual); } - + [TestMethod] public void RepeatOneCountShouldBeLength1String() { @@ -24,13 +24,13 @@ public class RuneTests Assert.AreEqual(1, repeated.Length); Assert.AreEqual("a", repeated); } - + [TestMethod] public void RepeatZeroCountShouldBeEmpty() { Assert.AreEqual(string.Empty, new Rune('a').Repeat(0)); } - + [TestMethod] public void RepeatNegativeCountShouldThrow() { diff --git a/X10D.Tests/src/Text/StringTests.cs b/X10D.Tests/src/Text/StringTests.cs index 794ceeb..6e13ab5 100644 --- a/X10D.Tests/src/Text/StringTests.cs +++ b/X10D.Tests/src/Text/StringTests.cs @@ -7,10 +7,42 @@ namespace X10D.Tests.Text; [TestClass] public class StringTests { - private struct SampleStructure + [TestMethod] + public void AsNullIfEmptyShouldBeCorrect() { - [JsonPropertyName("values")] - public int[] Values { get; set; } + const string sampleString = "Hello World"; + const string whitespaceString = " "; + const string emptyString = ""; + const string? nullString = null; + + string sampleResult = sampleString.AsNullIfEmpty(); + string whitespaceResult = whitespaceString.AsNullIfEmpty(); + string emptyResult = emptyString.AsNullIfEmpty(); + string? nullResult = nullString.AsNullIfEmpty(); + + Assert.AreEqual(sampleString, sampleResult); + Assert.AreEqual(whitespaceString, whitespaceResult); + Assert.AreEqual(nullString, emptyResult); + Assert.AreEqual(nullString, nullResult); + } + + [TestMethod] + public void AsNullIfWhiteSpaceShouldBeCorrect() + { + const string sampleString = "Hello World"; + const string whitespaceString = " "; + const string emptyString = ""; + const string? nullString = null; + + string sampleResult = sampleString.AsNullIfWhiteSpace(); + string whitespaceResult = whitespaceString.AsNullIfWhiteSpace(); + string emptyResult = emptyString.AsNullIfWhiteSpace(); + string? nullResult = nullString.AsNullIfWhiteSpace(); + + Assert.AreEqual(sampleString, sampleResult); + Assert.AreEqual(nullString, whitespaceResult); + Assert.AreEqual(nullString, emptyResult); + Assert.AreEqual(nullString, nullResult); } [TestMethod] @@ -26,4 +58,174 @@ public class StringTests Assert.AreEqual(2, target.Values[1]); Assert.AreEqual(3, target.Values[2]); } + + [TestMethod] + public void IsLowerShouldBeCorrect() + { + Assert.IsTrue("hello world".IsLower()); + Assert.IsFalse("HELLO WORLD".IsLower()); + Assert.IsFalse("Hello World".IsLower()); + } + + [TestMethod] + public void IsLowerNullShouldThrow() + { + Assert.ThrowsException(() => ((string?)null)!.IsLower()); + } + + [TestMethod] + public void IsPalindromeShouldBeCorrect() + { + const string inputA = "Race car"; + const string inputB = "Racecar"; + const string inputC = "A man, a plan, a canal, panama"; + const string inputD = "Jackdaws love my big sphinx of quartz"; + const string inputE = "Y"; + const string inputF = "1"; + + Assert.IsTrue(inputA.IsPalindrome(), inputA); + Assert.IsTrue(inputB.IsPalindrome(), inputB); + Assert.IsTrue(inputC.IsPalindrome(), inputC); + Assert.IsFalse(inputD.IsPalindrome(), inputD); + Assert.IsTrue(inputE.IsPalindrome(), inputE); + Assert.IsTrue(inputF.IsPalindrome(), inputF); + } + + [TestMethod] + public void IsPalindromeEmptyShouldBeFalse() + { + Assert.IsFalse(string.Empty.IsPalindrome()); + } + + [TestMethod] + public void IsPalindromeNullShouldThrow() + { + Assert.ThrowsException(() => ((string?)null)!.IsPalindrome()); + } + + [TestMethod] + public void IsUpperShouldBeCorrect() + { + Assert.IsTrue("HELLO WORLD".IsUpper()); + Assert.IsFalse("hello world".IsUpper()); + Assert.IsFalse("Hello World".IsUpper()); + } + + [TestMethod] + public void IsUpperNullShouldThrow() + { + Assert.ThrowsException(() => ((string?)null)!.IsUpper()); + } + + [TestMethod] + public void RepeatShouldBeCorrect() + { + const string expected = "aaaaaaaaaa"; + string actual = "a".Repeat(10); + + Assert.AreEqual(expected, actual); + } + + [TestMethod] + public void RepeatOneCountShouldBeLength1String() + { + string repeated = "a".Repeat(1); + Assert.AreEqual(1, repeated.Length); + Assert.AreEqual("a", repeated); + } + + [TestMethod] + public void RepeatZeroCountShouldBeEmpty() + { + Assert.AreEqual(string.Empty, "a".Repeat(0)); + } + + [TestMethod] + public void RepeatNegativeCountShouldThrow() + { + Assert.ThrowsException(() => "a".Repeat(-1)); + } + + [TestMethod] + public void RepeatNullShouldThrow() + { + Assert.ThrowsException(() => ((string?)null)!.Repeat(0)); + } + + [TestMethod] + public void ReverseShouldBeCorrect() + { + const string input = "Hello World"; + const string expected = "dlroW olleH"; + + string result = input.Reverse(); + + Assert.AreEqual(string.Empty.Reverse(), string.Empty); + Assert.AreEqual(" ".Reverse(), " "); + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void ReverseNullShouldThrow() + { + Assert.ThrowsException(() => ((string?)null)!.Reverse()); + } + + [TestMethod] + public void ShuffleShouldReorder() + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + string shuffled = alphabet; + + Assert.AreEqual(alphabet, shuffled); + + shuffled = alphabet.Shuffled(); + + Assert.AreNotEqual(alphabet, shuffled); + } + + [TestMethod] + public void NullShuffleShouldThrow() + { + Assert.ThrowsException(() => ((string?)null)!.Shuffled()); + } + + [TestMethod] + public void WithEmptyAlternativeShouldBeCorrect() + { + const string inputA = "Hello World"; + const string inputB = " "; + const string inputC = ""; + const string? inputD = null; + const string alternative = "ALTERNATIVE"; + + string resultA = inputA.WithEmptyAlternative(alternative); + string resultB = inputB.WithEmptyAlternative(alternative); + string resultC = inputC.WithEmptyAlternative(alternative); + string resultD = inputD.WithEmptyAlternative(alternative); + + Assert.AreEqual(resultA, inputA); + Assert.AreEqual(resultB, inputB); + Assert.AreEqual(resultC, alternative); + Assert.AreEqual(resultD, alternative); + Assert.AreEqual(alternative, ((string?)null).WithEmptyAlternative(alternative)); + } + + [TestMethod] + public void WithWhiteSpaceAlternativeShouldBeCorrect() + { + const string input = " "; + const string alternative = "ALTERNATIVE"; + + string result = input.WithWhiteSpaceAlternative(alternative); + + Assert.AreEqual(result, alternative); + Assert.AreEqual(alternative, ((string?)null).WithWhiteSpaceAlternative(alternative)); + } + + private struct SampleStructure + { + [JsonPropertyName("values")] + public int[] Values { get; set; } + } } diff --git a/X10D/src/ArrayExtensions/ArrayExtensions.cs b/X10D/src/Collections/ArrayExtensions.cs similarity index 99% rename from X10D/src/ArrayExtensions/ArrayExtensions.cs rename to X10D/src/Collections/ArrayExtensions.cs index 8968adc..a6db95e 100644 --- a/X10D/src/ArrayExtensions/ArrayExtensions.cs +++ b/X10D/src/Collections/ArrayExtensions.cs @@ -1,4 +1,4 @@ -namespace X10D; +namespace X10D.Collections; /// /// Extension methods for . diff --git a/X10D/src/StringExtensions/StringExtensions.cs b/X10D/src/StringExtensions/StringExtensions.cs index f16a9b8..6273969 100644 --- a/X10D/src/StringExtensions/StringExtensions.cs +++ b/X10D/src/StringExtensions/StringExtensions.cs @@ -1,6 +1,4 @@ -using System.Diagnostics.CodeAnalysis; using System.Text; -using X10D.Collections; namespace X10D; @@ -9,35 +7,6 @@ namespace X10D; /// public static class StringExtensions { - /// - /// Normalizes a string which may be either or empty to . - /// - /// The value to normalize. - /// - /// if is or empty; otherwise, - /// . - /// - [return: NotNullIfNotNull("value")] - public static string? AsNullIfEmpty(this string? value) - { - return value.WithEmptyAlternative(null); - } - - /// - /// Normalizes a string which may be either , empty, or consisting of only whitespace, to - /// . - /// - /// The value to normalize. - /// - /// if is , empty, or consists of only - /// whitespace; otherwise, . - /// - [return: NotNullIfNotNull("value")] - public static string? AsNullIfWhiteSpace(this string? value) - { - return value.WithWhiteSpaceAlternative(null); - } - /// /// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent plain text string. /// @@ -188,117 +157,6 @@ public static class StringExtensions return encoding.GetBytes(value); } - /// - /// Determines if all alpha characters in this string are considered lowercase. - /// - /// The input string. - /// - /// Returns if all alpha characters are lowercase, - /// otherwise. - /// - public static bool IsLower(this string value) - { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - - for (var index = 0; index < value.Length; index++) - { - if (!char.IsLetter(value[index])) - { - continue; - } - - if (!char.IsLower(value[index])) - { - return false; - } - } - - return true; - } - - /// - /// Determines whether the current string is considered palindromic; that is, the letters within the string are the - /// same when reversed. - /// - /// The value to check. - /// - /// if is considered a palindromic string; otherwise, - /// . - /// - /// is . - public static bool IsPalindrome(this string value) - { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (string.IsNullOrWhiteSpace(value)) - { - // empty string is not palindromic - return false; - } - - for (int index = 0, endIndex = value.Length - 1; index < value.Length; index++, endIndex--) - { - Rune startRune = new Rune(value[index]); - Rune endRune = new Rune(value[endIndex]); - - if (!Rune.IsLetter(startRune) && !Rune.IsNumber(startRune)) - { - endIndex++; - continue; - } - - if (!Rune.IsLetter(endRune) && !Rune.IsNumber(endRune)) - { - index--; - continue; - } - - if (Rune.ToUpperInvariant(startRune) != Rune.ToUpperInvariant(endRune)) - { - return false; - } - } - - return true; - } - - /// - /// Determines if all alpha characters in this string are considered uppercase. - /// - /// The input string. - /// - /// Returns if all alpha characters are uppercase, - /// otherwise. - /// - public static bool IsUpper(this string value) - { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - - for (var index = 0; index < value.Length; index++) - { - if (!char.IsLetter(value[index])) - { - continue; - } - - if (!char.IsUpper(value[index])) - { - return false; - } - } - - return true; - } - /// /// Returns a new string of a specified length by randomly selecting characters from the current string. /// @@ -342,102 +200,6 @@ public static class StringExtensions return builder.ToString(); } - /// - /// Repeats a string a specified number of times. - /// - /// The string to repeat. - /// The repeat count. - /// A string containing repeated times. - /// - /// is . - public static string Repeat(this string value, int count) - { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0); - } - - var builder = new StringBuilder(value.Length * count); - - for (var i = 0; i < count; i++) - { - builder.Append(value); - } - - return builder.ToString(); - } - - /// - /// Reverses the current string. - /// - /// The string to reverse. - /// A whose characters are that of in reverse order. - public static string Reverse(this string value) - { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (value.Length < 2) - { - return value; - } - - unsafe - { - char* array = stackalloc char[value.Length]; - - for (var index = 0; index < value.Length; index++) - { - array[index] = value[value.Length - index - 1]; - } - - return new string(array); - } - } - - /// - /// Shuffles the characters in the string using the instance. - /// - /// The string to shuffle. - /// A new containing the characters in , rearranged. - public static string Shuffled(this string value) - { - return value.Shuffled(Random.Shared); - } - - /// - /// Shuffles the characters in the string. - /// - /// The string to shuffle. - /// The instance. - /// A new containing the characters in , rearranged. - /// - /// is . - /// -or- - /// is . - /// - public static string Shuffled(this string value, Random random) - { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (random is null) - { - throw new ArgumentNullException(nameof(random)); - } - - return new string(value.ToCharArray().Shuffled(random).ToArray()); - } - /// /// Splits the into chunks that are no greater than in length. /// @@ -478,35 +240,4 @@ public static class StringExtensions ? result : default; } - - /// - /// Normalizes a string which may be either or empty to a specified alternative. - /// - /// The value to normalize. - /// The alternative string. - /// - /// if is or empty; otherwise, - /// . - /// - [return: NotNullIfNotNull("alternative")] - public static string? WithEmptyAlternative(this string? value, string? alternative) - { - return string.IsNullOrEmpty(value) ? alternative : value; - } - - /// - /// Normalizes a string which may be either , empty, or consisting of only whitespace, to a - /// specified alternative. - /// - /// The value to normalize. - /// The alternative string. - /// - /// if is , empty, or consists of only - /// whitespace; otherwise, . - /// - [return: NotNullIfNotNull("alternative")] - public static string? WithWhiteSpaceAlternative(this string? value, string? alternative) - { - return string.IsNullOrWhiteSpace(value) ? alternative : value; - } } diff --git a/X10D/src/Text/CharExtensions.cs b/X10D/src/Text/CharExtensions.cs index 3c2b23f..10d2111 100644 --- a/X10D/src/Text/CharExtensions.cs +++ b/X10D/src/Text/CharExtensions.cs @@ -1,7 +1,7 @@ namespace X10D.Text; /// -/// Extension methods for . +/// Text-related extension methods for . /// public static class CharExtensions { @@ -15,21 +15,12 @@ public static class CharExtensions /// public static string Repeat(this char value, int count) { - if (count < 0) + return count switch { - throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0); - } - - if (count == 0) - { - return string.Empty; - } - - if (count == 1) - { - return value.ToString(); - } - - return new string(value, count); + < 0 => throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0), + 0 => string.Empty, + 1 => value.ToString(), + _ => new string(value, count) + }; } } diff --git a/X10D/src/Text/RuneExtensions.cs b/X10D/src/Text/RuneExtensions.cs index 9441773..7a72818 100644 --- a/X10D/src/Text/RuneExtensions.cs +++ b/X10D/src/Text/RuneExtensions.cs @@ -3,7 +3,7 @@ namespace X10D.Text; /// -/// Extension methods for . +/// Text-related extension methods for . /// public static class RuneExtensions { @@ -17,19 +17,14 @@ public static class RuneExtensions /// public static string Repeat(this Rune value, int count) { - if (count < 0) + switch (count) { - throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0); - } - - if (count == 0) - { - return string.Empty; - } - - if (count == 1) - { - return value.ToString(); + case < 0: + throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0); + case 0: + return string.Empty; + case 1: + return value.ToString(); } int utf8SequenceLength = value.Utf8SequenceLength; diff --git a/X10D/src/Text/StringExtensions.cs b/X10D/src/Text/StringExtensions.cs index f4fd6fb..761016b 100644 --- a/X10D/src/Text/StringExtensions.cs +++ b/X10D/src/Text/StringExtensions.cs @@ -1,4 +1,7 @@ -using System.Text.Json; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Text.Json; +using X10D.Collections; namespace X10D.Text; @@ -7,6 +10,35 @@ namespace X10D.Text; /// public static class StringExtensions { + /// + /// Normalizes a string which may be either or empty to . + /// + /// The value to normalize. + /// + /// if is or empty; otherwise, + /// . + /// + [return: NotNullIfNotNull("value")] + public static string? AsNullIfEmpty(this string? value) + { + return value.WithEmptyAlternative(null); + } + + /// + /// Normalizes a string which may be either , empty, or consisting of only whitespace, to + /// . + /// + /// The value to normalize. + /// + /// if is , empty, or consists of only + /// whitespace; otherwise, . + /// + [return: NotNullIfNotNull("value")] + public static string? AsNullIfWhiteSpace(this string? value) + { + return value.WithWhiteSpaceAlternative(null); + } + /// /// Returns an object from the specified JSON string. /// @@ -20,4 +52,236 @@ public static class StringExtensions { return JsonSerializer.Deserialize(value, options); } + + /// + /// Determines if all alpha characters in this string are considered lowercase. + /// + /// The input string. + /// + /// Returns if all alpha characters are lowercase, + /// otherwise. + /// + public static bool IsLower(this string value) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + for (var index = 0; index < value.Length; index++) + { + var rune = new Rune(value[index]); + + if (!Rune.IsLetter(rune)) + { + continue; + } + + if (!Rune.IsLower(rune)) + { + return false; + } + } + + return true; + } + + /// + /// Determines whether the current string is considered palindromic; that is, the letters within the string are the + /// same when reversed. + /// + /// The value to check. + /// + /// if is considered a palindromic string; otherwise, + /// . + /// + /// is . + public static bool IsPalindrome(this string value) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (string.IsNullOrWhiteSpace(value)) + { + // empty string is not palindromic + return false; + } + + for (int index = 0, endIndex = value.Length - 1; index < value.Length; index++, endIndex--) + { + Rune startRune = new Rune(value[index]); + Rune endRune = new Rune(value[endIndex]); + + if (!Rune.IsLetter(startRune) && !Rune.IsNumber(startRune)) + { + endIndex++; + continue; + } + + if (!Rune.IsLetter(endRune) && !Rune.IsNumber(endRune)) + { + index--; + continue; + } + + if (Rune.ToUpperInvariant(startRune) != Rune.ToUpperInvariant(endRune)) + { + return false; + } + } + + return true; + } + + /// + /// Determines if all alpha characters in this string are considered uppercase. + /// + /// The input string. + /// + /// Returns if all alpha characters are uppercase, + /// otherwise. + /// + public static bool IsUpper(this string value) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + for (var index = 0; index < value.Length; index++) + { + var rune = new Rune(value[index]); + + if (!Rune.IsLetter(rune)) + { + continue; + } + + if (!Rune.IsUpper(rune)) + { + return false; + } + } + + return true; + } + + /// + /// Repeats a string a specified number of times. + /// + /// The string to repeat. + /// The repeat count. + /// A string containing repeated times. + /// + /// is . + public static string Repeat(this string value, int count) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + switch (count) + { + case < 0: + throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0); + case 0: + return string.Empty; + case 1: + return value; + } + + var builder = new StringBuilder(value.Length * count); + + for (var i = 0; i < count; i++) + { + builder.Append(value); + } + + return builder.ToString(); + } + + /// + /// Reverses the current string. + /// + /// The string to reverse. + /// A whose characters are that of in reverse order. + public static string Reverse(this string value) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (value.Length < 2) + { + return value; + } + + Span span = stackalloc char[value.Length]; + + for (var index = 0; index < value.Length; index++) + { + span[index] = value[value.Length - index - 1]; + } + + return new string(span); + } + + /// + /// Shuffles the characters in the string. + /// + /// The string to shuffle. + /// + /// The instance to use for the shuffling. If is specified, + /// is used. + /// + /// A new containing the characters in , rearranged. + /// is . + public static string Shuffled(this string value, Random? random = null) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + random ??= Random.Shared; + + char[] array = value.ToCharArray(); + array.Shuffle(random); + return new string(array); + } + + /// + /// Normalizes a string which may be either or empty to a specified alternative. + /// + /// The value to normalize. + /// The alternative string. + /// + /// if is or empty; otherwise, + /// . + /// + [return: NotNullIfNotNull("alternative")] + public static string? WithEmptyAlternative(this string? value, string? alternative) + { + return string.IsNullOrEmpty(value) ? alternative : value; + } + + /// + /// Normalizes a string which may be either , empty, or consisting of only whitespace, to a + /// specified alternative. + /// + /// The value to normalize. + /// The alternative string. + /// + /// if is , empty, or consists of only + /// whitespace; otherwise, . + /// + [return: NotNullIfNotNull("alternative")] + public static string? WithWhiteSpaceAlternative(this string? value, string? alternative) + { + return string.IsNullOrWhiteSpace(value) ? alternative : value; + } }