feat: add string.EnsureEndsWith and string.EnsureStartsWith

This commit is contained in:
Oliver Booth 2023-03-30 02:09:04 +01:00
parent f5b53cd3f6
commit f49188b428
No known key found for this signature in database
GPG Key ID: 20BEB9DC87961025
3 changed files with 263 additions and 1 deletions

View File

@ -69,6 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- X10D: Added `Span<T>.Split(Span<T>)`. - X10D: Added `Span<T>.Split(Span<T>)`.
- X10D: Added `string.CountSubstring(char)`. - X10D: Added `string.CountSubstring(char)`.
- X10D: Added `string.CountSubstring(string[, StringComparison])`. - X10D: Added `string.CountSubstring(string[, StringComparison])`.
- X10D: Added `string.EnsureStartsWith()` and `string.EnsureEndsWith()`.
- X10D: Added `string.IsEmpty()`. - X10D: Added `string.IsEmpty()`.
- X10D: Added `string.IsWhiteSpace()`. - X10D: Added `string.IsWhiteSpace()`.
- X10D: Added `string.IsNullOrEmpty()`. - X10D: Added `string.IsNullOrEmpty()`.

View File

@ -142,6 +142,76 @@ public class StringTests
((string?)null!).CountSubstring(string.Empty, StringComparison.OrdinalIgnoreCase)); ((string?)null!).CountSubstring(string.Empty, StringComparison.OrdinalIgnoreCase));
} }
[TestMethod]
public void EnsureEndsWith_ShouldPrependChar_GivenEndsWithReturnFalse()
{
const string value = "Hello Worl";
const char substring = 'd';
Assert.AreEqual("Hello World", value.EnsureEndsWith(substring));
}
[TestMethod]
public void EnsureEndsWith_ShouldReturnChar_GivenEndsWithReturnTrue()
{
const string value = "A";
const char substring = 'A';
Assert.AreEqual(value, value.EnsureEndsWith(substring));
}
[TestMethod]
public void EnsureStartsWith_ShouldPrependChar_GivenEndsWithReturnFalse()
{
const string value = "B";
const char substring = 'A';
Assert.AreEqual("AB", value.EnsureStartsWith(substring));
}
[TestMethod]
public void EnsureStartsWith_ShouldReturnChar_GivenEndsWithReturnTrue()
{
const string value = "A";
const char substring = 'A';
Assert.AreEqual(value, value.EnsureStartsWith(substring));
}
[TestMethod]
public void EnsureEndsWith_ShouldAppendSubstring_GivenEndsWithReturnFalse()
{
const string value = "Hello ";
const string substring = "World";
Assert.AreEqual("Hello World", value.EnsureEndsWith(substring));
}
[TestMethod]
public void EnsureEndsWith_ShouldReturnString_GivenEndsWithReturnTrue()
{
const string substring = "World";
Assert.AreEqual(substring, substring.EnsureEndsWith(substring));
}
[TestMethod]
public void EnsureStartsWith_ShouldAppendSubstring_GivenEndsWithReturnFalse()
{
const string value = "World";
const string substring = "Hello ";
Assert.AreEqual("Hello World", value.EnsureStartsWith(substring));
}
[TestMethod]
public void EnsureStartsWith_ShouldReturnString_GivenEndsWithReturnTrue()
{
const string substring = "World";
Assert.AreEqual(substring, substring.EnsureStartsWith(substring));
}
[TestMethod] [TestMethod]
public void EnumParse_ShouldReturnCorrectValue_GivenString() public void EnumParse_ShouldReturnCorrectValue_GivenString()
{ {

View File

@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
@ -251,6 +251,118 @@ public static class StringExtensions
return haystack.AsSpan().CountSubstring(needle, comparison); return haystack.AsSpan().CountSubstring(needle, comparison);
} }
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <returns>The combined string.</returns>
public static string EnsureEndsWith(this string? value, char substring)
{
return EnsureEndsWith(value, substring, StringComparison.Ordinal);
}
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <param name="comparisonType">One of the enumeration values that determines how the substring is compared.</param>
/// <returns>The combined string.</returns>
public static string EnsureEndsWith(this string? value, char substring, StringComparison comparisonType)
{
return EnsureEndsWith(value, substring.ToString(), comparisonType);
}
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <returns>The combined string.</returns>
public static string EnsureEndsWith(this string? value, string substring)
{
return EnsureEndsWith(value, substring, StringComparison.Ordinal);
}
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <param name="comparisonType">One of the enumeration values that determines how the substring is compared.</param>
/// <returns>The combined string.</returns>
public static string EnsureEndsWith(this string? value, string substring, StringComparison comparisonType)
{
if (string.IsNullOrEmpty(value))
{
return substring;
}
if (value.EndsWith(substring, comparisonType))
{
return value;
}
return value + substring;
}
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <returns>The combined string.</returns>
public static string EnsureStartsWith(this string? value, char substring)
{
return EnsureStartsWith(value, substring, StringComparison.Ordinal);
}
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <param name="comparisonType">One of the enumeration values that determines how the substring is compared.</param>
/// <returns>The combined string.</returns>
public static string EnsureStartsWith(this string? value, char substring, StringComparison comparisonType)
{
return EnsureStartsWith(value, substring.ToString(), comparisonType);
}
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <returns>The combined string.</returns>
public static string EnsureStartsWith(this string? value, string substring)
{
return EnsureStartsWith(value, substring, StringComparison.Ordinal);
}
/// <summary>
/// Ensures that the current string starts with a specified substring.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="substring">The substring to prepend, if the current string does not already start with it.</param>
/// <param name="comparisonType">One of the enumeration values that determines how the substring is compared.</param>
/// <returns>The combined string.</returns>
public static string EnsureStartsWith(this string? value, string substring, StringComparison comparisonType)
{
if (string.IsNullOrEmpty(value))
{
return substring;
}
if (value.StartsWith(substring, comparisonType))
{
return value;
}
return substring + value;
}
/// <summary> /// <summary>
/// Parses a <see cref="string" /> into an <see cref="Enum" />. /// Parses a <see cref="string" /> into an <see cref="Enum" />.
/// </summary> /// </summary>
@ -444,6 +556,7 @@ public static class StringExtensions
/// <returns> /// <returns>
/// <see langword="true" /> if all alpha characters in this string are lowercase; otherwise, <see langword="false" />. /// <see langword="true" /> if all alpha characters in this string are lowercase; otherwise, <see langword="false" />.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
[Pure] [Pure]
#if NETSTANDARD2_1 #if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -921,6 +1034,84 @@ public static class StringExtensions
} }
} }
/// <summary>
/// Determines whether the beginning of this string instance matches any of the specified strings using the current
/// culture for comparison.
/// </summary>
/// <param name="value">The value to compare.</param>
/// <param name="startValues">An array of string to compare.</param>
/// <returns>
/// <see langword="true" /> if <paramref name="value" /> starts with any of the <paramref name="startValues" />;
/// otherwise, <see langword="false" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="startValues" />, or at least one of its elements, is <see langword="null" />.
/// </exception>
public static bool StartsWithAny(this string? value, params string[] startValues)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(startValues);
#else
if (startValues is null)
{
throw new ArgumentNullException(nameof(startValues));
}
#endif
if (startValues.Length == 0 || string.IsNullOrWhiteSpace(value))
{
return false;
}
return value.StartsWithAny(StringComparison.CurrentCulture, startValues);
}
/// <summary>
/// Determines whether the beginning of this string instance matches any of the specified strings when compared using the
/// specified comparison option.
/// </summary>
/// <param name="value">The value to compare.</param>
/// <param name="comparison">One of the enumeration values that determines how this string and value are compared.</param>
/// <param name="startValues">An array of string to compare.</param>
/// <returns>
/// <see langword="true" /> if <paramref name="value" /> starts with any of the <paramref name="startValues" />;
/// otherwise, <see langword="false" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="startValues" />, or at least one of its elements, is <see langword="null" />.
/// </exception>
public static bool StartsWithAny(this string? value, StringComparison comparison, params string[] startValues)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(startValues);
#else
if (startValues is null)
{
throw new ArgumentNullException(nameof(startValues));
}
#endif
if (startValues.Length == 0 || string.IsNullOrWhiteSpace(value))
{
return false;
}
for (var index = 0; index < startValues.Length; index++)
{
if (startValues[index] is null)
{
throw new ArgumentNullException(nameof(startValues));
}
if (value.StartsWith(startValues[index], comparison))
{
return true;
}
}
return false;
}
/// <summary> /// <summary>
/// Normalizes a string which may be either <see langword="null" /> or empty to a specified alternative. /// Normalizes a string which may be either <see langword="null" /> or empty to a specified alternative.
/// </summary> /// </summary>