1
0
mirror of https://github.com/oliverbooth/X10D synced 2024-11-22 23:58:48 +00:00

Move additional methods into child namespaces (#7)

This commit is contained in:
Oliver Booth 2022-04-24 10:09:56 +01:00
parent efaf93594a
commit 30e925cdb0
No known key found for this signature in database
GPG Key ID: 32A00B35503AF634
11 changed files with 596 additions and 546 deletions

View File

@ -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<object>);
}
[TestMethod]
public void AsReadOnlyNullShouldThrow()
{
object[]? array = null;
Assert.ThrowsException<ArgumentNullException>(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<ArgumentNullException>(array!.Clear);
Assert.ThrowsException<ArgumentNullException>(() => 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<ArgumentNullException>(() => array!.Fill(0));
Assert.ThrowsException<ArgumentNullException>(() => 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..]);
}
}

View File

@ -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<ArgumentNullException>(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<ArgumentNullException>(() => array!.Fill(0));
}
}

View File

@ -1,6 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Core; using X10D.Core;
using X10D.Text;
namespace X10D.Tests.Core; namespace X10D.Tests.Core;

View File

@ -8,50 +8,6 @@ namespace X10D.Tests.Core;
[TestClass] [TestClass]
public class StringTests public class StringTests
{ {
/// <summary>
/// Tests <see cref="StringExtensions.AsNullIfEmpty" />.
/// </summary>
[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);
}
/// <summary>
/// Tests <see cref="StringExtensions.AsNullIfWhiteSpace" />.
/// </summary>
[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);
}
/// <summary> /// <summary>
/// Tests <see cref="StringExtensions.Base64Decode" />. /// Tests <see cref="StringExtensions.Base64Decode" />.
/// </summary> /// </summary>
@ -81,78 +37,6 @@ public class StringTests
Assert.AreEqual(expected, result); Assert.AreEqual(expected, result);
} }
/// <summary>
/// Tests <see cref="StringExtensions.IsLower" />.
/// </summary>
[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<ArgumentNullException>(() => 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);
}
/// <summary>
/// Tests <see cref="StringExtensions.IsUpper" />.
/// </summary>
[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<ArgumentNullException>(() => nullString!.IsUpper());
Assert.IsFalse(resultA);
Assert.IsTrue(resultB);
}
/// <summary>
/// Tests <see cref="StringExtensions.Shuffled(string, Random)" />.
/// </summary>
[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<ArgumentNullException>(() => ((string?)null)!.Shuffled());
Assert.AreEqual(expected, result);
}
/// <summary> /// <summary>
/// Tests <see cref="StringExtensions.Randomize" />. /// Tests <see cref="StringExtensions.Randomize" />.
/// </summary> /// </summary>
@ -170,66 +54,4 @@ public class StringTests
Assert.AreEqual(string.Empty, string.Empty.Randomize(0)); Assert.AreEqual(string.Empty, string.Empty.Randomize(0));
Assert.AreEqual(expected, result); Assert.AreEqual(expected, result);
} }
}
/// <summary>
/// Tests <see cref="StringExtensions.Repeat" />.
/// </summary>
[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<ArgumentNullException>(() => ((string?)null)!.Repeat(repeatCount));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => input.Repeat(-1));
Assert.AreEqual(expected, result);
}
/// <summary>
/// Tests <see cref="StringExtensions.Repeat" />.
/// </summary>
[TestMethod]
public void Reverse()
{
const string input = "Hello World";
const string expected = "dlroW olleH";
string result = input.Reverse();
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.Reverse());
Assert.AreEqual(string.Empty.Reverse(), string.Empty);
Assert.AreEqual(" ".Reverse(), " ");
Assert.AreEqual(expected, result);
}
/// <summary>
/// Tests <see cref="StringExtensions.WithEmptyAlternative" /> and
/// <see cref="StringExtensions.WithWhiteSpaceAlternative"/>.
/// </summary>
[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));
}
}

View File

@ -16,7 +16,7 @@ public class RuneTests
Assert.AreEqual(expected, actual); Assert.AreEqual(expected, actual);
} }
[TestMethod] [TestMethod]
public void RepeatOneCountShouldBeLength1String() public void RepeatOneCountShouldBeLength1String()
{ {
@ -24,13 +24,13 @@ public class RuneTests
Assert.AreEqual(1, repeated.Length); Assert.AreEqual(1, repeated.Length);
Assert.AreEqual("a", repeated); Assert.AreEqual("a", repeated);
} }
[TestMethod] [TestMethod]
public void RepeatZeroCountShouldBeEmpty() public void RepeatZeroCountShouldBeEmpty()
{ {
Assert.AreEqual(string.Empty, new Rune('a').Repeat(0)); Assert.AreEqual(string.Empty, new Rune('a').Repeat(0));
} }
[TestMethod] [TestMethod]
public void RepeatNegativeCountShouldThrow() public void RepeatNegativeCountShouldThrow()
{ {

View File

@ -7,10 +7,42 @@ namespace X10D.Tests.Text;
[TestClass] [TestClass]
public class StringTests public class StringTests
{ {
private struct SampleStructure [TestMethod]
public void AsNullIfEmptyShouldBeCorrect()
{ {
[JsonPropertyName("values")] const string sampleString = "Hello World";
public int[] Values { get; set; } 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] [TestMethod]
@ -26,4 +58,174 @@ public class StringTests
Assert.AreEqual(2, target.Values[1]); Assert.AreEqual(2, target.Values[1]);
Assert.AreEqual(3, target.Values[2]); 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<ArgumentNullException>(() => ((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<ArgumentNullException>(() => ((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<ArgumentNullException>(() => ((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<ArgumentOutOfRangeException>(() => "a".Repeat(-1));
}
[TestMethod]
public void RepeatNullShouldThrow()
{
Assert.ThrowsException<ArgumentNullException>(() => ((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<ArgumentNullException>(() => ((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<ArgumentNullException>(() => ((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; }
}
} }

View File

@ -1,4 +1,4 @@
namespace X10D; namespace X10D.Collections;
/// <summary> /// <summary>
/// Extension methods for <see cref="Array" />. /// Extension methods for <see cref="Array" />.

View File

@ -1,6 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Text; using System.Text;
using X10D.Collections;
namespace X10D; namespace X10D;
@ -9,35 +7,6 @@ namespace X10D;
/// </summary> /// </summary>
public static class StringExtensions public static class StringExtensions
{ {
/// <summary>
/// Normalizes a string which may be either <see langword="null" /> or empty to <see langword="null" />.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <returns>
/// <see langword="null" /> if <paramref name="value" /> is <see langword="null" /> or empty; otherwise,
/// <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("value")]
public static string? AsNullIfEmpty(this string? value)
{
return value.WithEmptyAlternative(null);
}
/// <summary>
/// Normalizes a string which may be either <see langword="null" />, empty, or consisting of only whitespace, to
/// <see langword="null" />.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <returns>
/// <see langword="null" /> if <paramref name="value" /> is <see langword="null" />, empty, or consists of only
/// whitespace; otherwise, <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("value")]
public static string? AsNullIfWhiteSpace(this string? value)
{
return value.WithWhiteSpaceAlternative(null);
}
/// <summary> /// <summary>
/// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent plain text string. /// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent plain text string.
/// </summary> /// </summary>
@ -188,117 +157,6 @@ public static class StringExtensions
return encoding.GetBytes(value); return encoding.GetBytes(value);
} }
/// <summary>
/// Determines if all alpha characters in this string are considered lowercase.
/// </summary>
/// <param name="value">The input string.</param>
/// <returns>
/// Returns <see langword="true" /> if all alpha characters are lowercase, <see langword="false" />
/// otherwise.
/// </returns>
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;
}
/// <summary>
/// Determines whether the current string is considered palindromic; that is, the letters within the string are the
/// same when reversed.
/// </summary>
/// <param name="value">The value to check.</param>
/// <returns>
/// <see langword="true" /> if <paramref name="value" /> is considered a palindromic string; otherwise,
/// <see langword="false" />.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
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;
}
/// <summary>
/// Determines if all alpha characters in this string are considered uppercase.
/// </summary>
/// <param name="value">The input string.</param>
/// <returns>
/// Returns <see langword="true" /> if all alpha characters are uppercase, <see langword="false" />
/// otherwise.
/// </returns>
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;
}
/// <summary> /// <summary>
/// Returns a new string of a specified length by randomly selecting characters from the current string. /// Returns a new string of a specified length by randomly selecting characters from the current string.
/// </summary> /// </summary>
@ -342,102 +200,6 @@ public static class StringExtensions
return builder.ToString(); return builder.ToString();
} }
/// <summary>
/// Repeats a string a specified number of times.
/// </summary>
/// <param name="value">The string to repeat.</param>
/// <param name="count">The repeat count.</param>
/// <returns>A string containing <paramref name="value" /> repeated <paramref name="count" /> times.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
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();
}
/// <summary>
/// Reverses the current string.
/// </summary>
/// <param name="value">The string to reverse.</param>
/// <returns>A <see cref="string" /> whose characters are that of <paramref name="value" /> in reverse order.</returns>
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);
}
}
/// <summary>
/// Shuffles the characters in the string using the <see cref="Random.Shared" /> instance.
/// </summary>
/// <param name="value">The string to shuffle.</param>
/// <returns>A new <see cref="string" /> containing the characters in <paramref name="value" />, rearranged.</returns>
public static string Shuffled(this string value)
{
return value.Shuffled(Random.Shared);
}
/// <summary>
/// Shuffles the characters in the string.
/// </summary>
/// <param name="value">The string to shuffle.</param>
/// <param name="random">The <see cref="System.Random" /> instance.</param>
/// <returns>A new <see cref="string" /> containing the characters in <paramref name="value" />, rearranged.</returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="value" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="random" /> is <see langword="null" />.</para>
/// </exception>
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());
}
/// <summary> /// <summary>
/// Splits the <see cref="string" /> into chunks that are no greater than <paramref name="chunkSize" /> in length. /// Splits the <see cref="string" /> into chunks that are no greater than <paramref name="chunkSize" /> in length.
/// </summary> /// </summary>
@ -478,35 +240,4 @@ public static class StringExtensions
? result ? result
: default; : default;
} }
/// <summary>
/// Normalizes a string which may be either <see langword="null" /> or empty to a specified alternative.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <param name="alternative">The alternative string.</param>
/// <returns>
/// <paramref name="alternative" /> if <paramref name="value" /> is <see langword="null" /> or empty; otherwise,
/// <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("alternative")]
public static string? WithEmptyAlternative(this string? value, string? alternative)
{
return string.IsNullOrEmpty(value) ? alternative : value;
}
/// <summary>
/// Normalizes a string which may be either <see langword="null" />, empty, or consisting of only whitespace, to a
/// specified alternative.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <param name="alternative">The alternative string.</param>
/// <returns>
/// <paramref name="alternative" /> if <paramref name="value" /> is <see langword="null" />, empty, or consists of only
/// whitespace; otherwise, <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("alternative")]
public static string? WithWhiteSpaceAlternative(this string? value, string? alternative)
{
return string.IsNullOrWhiteSpace(value) ? alternative : value;
}
} }

View File

@ -1,7 +1,7 @@
namespace X10D.Text; namespace X10D.Text;
/// <summary> /// <summary>
/// Extension methods for <see cref="char" />. /// Text-related extension methods for <see cref="char" />.
/// </summary> /// </summary>
public static class CharExtensions public static class CharExtensions
{ {
@ -15,21 +15,12 @@ public static class CharExtensions
/// </returns> /// </returns>
public static string Repeat(this char value, int count) public static string Repeat(this char value, int count)
{ {
if (count < 0) return count switch
{ {
throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0); < 0 => throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0),
} 0 => string.Empty,
1 => value.ToString(),
if (count == 0) _ => new string(value, count)
{ };
return string.Empty;
}
if (count == 1)
{
return value.ToString();
}
return new string(value, count);
} }
} }

View File

@ -3,7 +3,7 @@
namespace X10D.Text; namespace X10D.Text;
/// <summary> /// <summary>
/// Extension methods for <see cref="Rune" />. /// Text-related extension methods for <see cref="Rune" />.
/// </summary> /// </summary>
public static class RuneExtensions public static class RuneExtensions
{ {
@ -17,19 +17,14 @@ public static class RuneExtensions
/// </returns> /// </returns>
public static string Repeat(this Rune value, int count) public static string Repeat(this Rune value, int count)
{ {
if (count < 0) switch (count)
{ {
throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0); case < 0:
} throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0);
case 0:
if (count == 0) return string.Empty;
{ case 1:
return string.Empty; return value.ToString();
}
if (count == 1)
{
return value.ToString();
} }
int utf8SequenceLength = value.Utf8SequenceLength; int utf8SequenceLength = value.Utf8SequenceLength;

View File

@ -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; namespace X10D.Text;
@ -7,6 +10,35 @@ namespace X10D.Text;
/// </summary> /// </summary>
public static class StringExtensions public static class StringExtensions
{ {
/// <summary>
/// Normalizes a string which may be either <see langword="null" /> or empty to <see langword="null" />.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <returns>
/// <see langword="null" /> if <paramref name="value" /> is <see langword="null" /> or empty; otherwise,
/// <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("value")]
public static string? AsNullIfEmpty(this string? value)
{
return value.WithEmptyAlternative(null);
}
/// <summary>
/// Normalizes a string which may be either <see langword="null" />, empty, or consisting of only whitespace, to
/// <see langword="null" />.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <returns>
/// <see langword="null" /> if <paramref name="value" /> is <see langword="null" />, empty, or consists of only
/// whitespace; otherwise, <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("value")]
public static string? AsNullIfWhiteSpace(this string? value)
{
return value.WithWhiteSpaceAlternative(null);
}
/// <summary> /// <summary>
/// Returns an object from the specified JSON string. /// Returns an object from the specified JSON string.
/// </summary> /// </summary>
@ -20,4 +52,236 @@ public static class StringExtensions
{ {
return JsonSerializer.Deserialize<T>(value, options); return JsonSerializer.Deserialize<T>(value, options);
} }
/// <summary>
/// Determines if all alpha characters in this string are considered lowercase.
/// </summary>
/// <param name="value">The input string.</param>
/// <returns>
/// Returns <see langword="true" /> if all alpha characters are lowercase, <see langword="false" />
/// otherwise.
/// </returns>
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;
}
/// <summary>
/// Determines whether the current string is considered palindromic; that is, the letters within the string are the
/// same when reversed.
/// </summary>
/// <param name="value">The value to check.</param>
/// <returns>
/// <see langword="true" /> if <paramref name="value" /> is considered a palindromic string; otherwise,
/// <see langword="false" />.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
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;
}
/// <summary>
/// Determines if all alpha characters in this string are considered uppercase.
/// </summary>
/// <param name="value">The input string.</param>
/// <returns>
/// Returns <see langword="true" /> if all alpha characters are uppercase, <see langword="false" />
/// otherwise.
/// </returns>
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;
}
/// <summary>
/// Repeats a string a specified number of times.
/// </summary>
/// <param name="value">The string to repeat.</param>
/// <param name="count">The repeat count.</param>
/// <returns>A string containing <paramref name="value" /> repeated <paramref name="count" /> times.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
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();
}
/// <summary>
/// Reverses the current string.
/// </summary>
/// <param name="value">The string to reverse.</param>
/// <returns>A <see cref="string" /> whose characters are that of <paramref name="value" /> in reverse order.</returns>
public static string Reverse(this string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
if (value.Length < 2)
{
return value;
}
Span<char> span = stackalloc char[value.Length];
for (var index = 0; index < value.Length; index++)
{
span[index] = value[value.Length - index - 1];
}
return new string(span);
}
/// <summary>
/// Shuffles the characters in the string.
/// </summary>
/// <param name="value">The string to shuffle.</param>
/// <param name="random">
/// The <see cref="System.Random" /> instance to use for the shuffling. If <see langword="null" /> is specified,
/// <see cref="System.Random.Shared" /> is used.
/// </param>
/// <returns>A new <see cref="string" /> containing the characters in <paramref name="value" />, rearranged.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
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);
}
/// <summary>
/// Normalizes a string which may be either <see langword="null" /> or empty to a specified alternative.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <param name="alternative">The alternative string.</param>
/// <returns>
/// <paramref name="alternative" /> if <paramref name="value" /> is <see langword="null" /> or empty; otherwise,
/// <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("alternative")]
public static string? WithEmptyAlternative(this string? value, string? alternative)
{
return string.IsNullOrEmpty(value) ? alternative : value;
}
/// <summary>
/// Normalizes a string which may be either <see langword="null" />, empty, or consisting of only whitespace, to a
/// specified alternative.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <param name="alternative">The alternative string.</param>
/// <returns>
/// <paramref name="alternative" /> if <paramref name="value" /> is <see langword="null" />, empty, or consists of only
/// whitespace; otherwise, <paramref name="value" />.
/// </returns>
[return: NotNullIfNotNull("alternative")]
public static string? WithWhiteSpaceAlternative(this string? value, string? alternative)
{
return string.IsNullOrWhiteSpace(value) ? alternative : value;
}
} }