mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-22 19:28:48 +00:00
Migrate string extensions to appropriate namespaces (#7)
Introduces more tests
This commit is contained in:
parent
c13cc934b6
commit
a6139a5720
@ -1,57 +0,0 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace X10D.Tests.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="StringExtensions" />.
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class StringTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests <see cref="StringExtensions.Base64Decode" />.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Base64Decode()
|
||||
{
|
||||
const string input = "SGVsbG8gV29ybGQ=";
|
||||
const string expected = "Hello World";
|
||||
|
||||
string result = input.Base64Decode();
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tests <see cref="StringExtensions.Base64Encode" />.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Base64Encode()
|
||||
{
|
||||
const string input = "Hello World";
|
||||
const string expected = "SGVsbG8gV29ybGQ=";
|
||||
|
||||
string result = input.Base64Encode();
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests <see cref="StringExtensions.Randomize" />.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Randomize()
|
||||
{
|
||||
const string input = "Hello World";
|
||||
const string expected = "le rooldeoH";
|
||||
var random = new Random(1);
|
||||
|
||||
string result = input.Randomize(input.Length, random);
|
||||
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.Randomize(1));
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => input.Randomize(-1));
|
||||
Assert.AreEqual(string.Empty, string.Empty.Randomize(0));
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
}
|
271
X10D.Tests/src/Text/StringBuilderReaderTests.cs
Normal file
271
X10D.Tests/src/Text/StringBuilderReaderTests.cs
Normal file
@ -0,0 +1,271 @@
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Text;
|
||||
|
||||
namespace X10D.Tests.Text;
|
||||
|
||||
[TestClass]
|
||||
public class StringBuilderReaderTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Peek_ShouldReturnNextChar_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Assert.AreEqual('H', reader.Peek());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldReturnNextChar_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Assert.AreEqual('H', reader.Read());
|
||||
Assert.AreEqual('e', reader.Read());
|
||||
Assert.AreEqual('l', reader.Read());
|
||||
Assert.AreEqual('l', reader.Read());
|
||||
Assert.AreEqual('o', reader.Read());
|
||||
Assert.AreEqual('\n', reader.Read());
|
||||
Assert.AreEqual('W', reader.Read());
|
||||
Assert.AreEqual('o', reader.Read());
|
||||
Assert.AreEqual('r', reader.Read());
|
||||
Assert.AreEqual('l', reader.Read());
|
||||
Assert.AreEqual('d', reader.Read());
|
||||
Assert.AreEqual(-1, reader.Read());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldPopulateArray_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
var array = new char[5];
|
||||
int read = reader.Read(array, 0, 5);
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), array);
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldReturnNegative1_GivenEndOfReader()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
var array = new char[11];
|
||||
reader.Read(array);
|
||||
Assert.AreEqual(-1, reader.Read(array, 0, 1));
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldThrow_GivenNullArray()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() =>
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
reader.Read(null!, 0, 5);
|
||||
reader.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldThrow_GivenNegativeIndex()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
var array = new char[5];
|
||||
reader.Read(array, -1, 5);
|
||||
reader.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldThrow_GivenNegativeCount()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
var array = new char[5];
|
||||
reader.Read(array, 0, -1);
|
||||
reader.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldThrow_GivenSmallBuffer()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() =>
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
var array = new char[1];
|
||||
reader.Read(array, 0, 5);
|
||||
reader.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_ShouldPopulateSpan_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Span<char> span = stackalloc char[5];
|
||||
int read = reader.Read(span);
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), span.ToArray());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadAsync_ShouldPopulateArray_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
var array = new char[5];
|
||||
int read = reader.ReadAsync(array, 0, 5).GetAwaiter().GetResult();
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), array);
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadAsync_ShouldPopulateMemory_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Memory<char> memory = new char[5];
|
||||
int read = reader.ReadAsync(memory).GetAwaiter().GetResult();
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), memory.ToArray());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadBlock_ShouldPopulateArray_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
var array = new char[5];
|
||||
int read = reader.ReadBlock(array, 0, 5);
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), array);
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadBlock_ShouldPopulateSpan_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Span<char> span = stackalloc char[5];
|
||||
int read = reader.ReadBlock(span);
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), span.ToArray());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadBlock_ShouldReturnNegative1_GivenEndOfReader()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
var array = new char[11];
|
||||
reader.Read(array);
|
||||
|
||||
int read = reader.ReadBlock(array, 0, 5);
|
||||
Assert.AreEqual(-1, read);
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadBlockAsync_ShouldPopulateArray_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
var array = new char[5];
|
||||
int read = reader.ReadBlockAsync(array, 0, 5).GetAwaiter().GetResult();
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), array);
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadBlockAsync_ShouldPopulateMemory_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Memory<char> memory = new char[5];
|
||||
int read = reader.ReadBlockAsync(memory).GetAwaiter().GetResult();
|
||||
Assert.AreEqual(5, read);
|
||||
|
||||
CollectionAssert.AreEqual("Hello".ToCharArray(), memory.ToArray());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadToEnd_ShouldReturnSourceString_GivenBuilder()
|
||||
{
|
||||
const string value = "Hello World";
|
||||
using var reader = new StringBuilderReader(new StringBuilder(value));
|
||||
Assert.AreEqual(value, reader.ReadToEnd());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadToEndAsync_ShouldReturnSourceString_GivenBuilder()
|
||||
{
|
||||
const string value = "Hello World";
|
||||
using var reader = new StringBuilderReader(new StringBuilder(value));
|
||||
Assert.AreEqual(value, reader.ReadToEndAsync().GetAwaiter().GetResult());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadLine_ShouldReturnSourceString_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Assert.AreEqual("Hello", reader.ReadLine());
|
||||
Assert.AreEqual("World", reader.ReadLine());
|
||||
Assert.AreEqual(null, reader.ReadLine());
|
||||
|
||||
Assert.AreEqual(-1, reader.Peek());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadLineAsync_ShouldReturnSourceString_GivenBuilder()
|
||||
{
|
||||
using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
|
||||
|
||||
Assert.AreEqual("Hello", reader.ReadLineAsync().GetAwaiter().GetResult());
|
||||
Assert.AreEqual("World", reader.ReadLineAsync().GetAwaiter().GetResult());
|
||||
Assert.AreEqual(null, reader.ReadLineAsync().GetAwaiter().GetResult());
|
||||
|
||||
Assert.AreEqual(-1, reader.Peek());
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Text;
|
||||
|
||||
@ -8,7 +9,7 @@ namespace X10D.Tests.Text;
|
||||
public class StringTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void AsNullIfEmptyShouldBeCorrect()
|
||||
public void AsNullIfEmpty_ShouldBeCorrect()
|
||||
{
|
||||
const string sampleString = "Hello World";
|
||||
const string whitespaceString = " ";
|
||||
@ -27,7 +28,7 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AsNullIfWhiteSpaceShouldBeCorrect()
|
||||
public void AsNullIfWhiteSpace_ShouldBeCorrect()
|
||||
{
|
||||
const string sampleString = "Hello World";
|
||||
const string whitespaceString = " ";
|
||||
@ -46,7 +47,110 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FromJsonShouldDeserializeCorrectly()
|
||||
public void Base64Decode_ShouldReturnHelloWorld_GivenBase64String()
|
||||
{
|
||||
Assert.AreEqual("Hello World", "SGVsbG8gV29ybGQ=".Base64Decode());
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Base64Decode_ShouldThrow_GivenNull()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.Base64Decode());
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Base64Encode_ShouldReturnBase64String_GivenHelloWorld()
|
||||
{
|
||||
Assert.AreEqual("SGVsbG8gV29ybGQ=", "Hello World".Base64Encode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Base64Encode_ShouldThrow_GivenNull()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.Base64Encode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ChangeEncoding_ShouldReturnAsciiString_GivenUtf8()
|
||||
{
|
||||
Assert.AreEqual("Hello World", "Hello World".ChangeEncoding(Encoding.UTF8, Encoding.ASCII));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ChangeEncoding_ShouldThrow_GivenNullString()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.ChangeEncoding(Encoding.UTF8, Encoding.ASCII));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ChangeEncoding_ShouldThrow_GivenNullSourceEncoding()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => "Hello World".ChangeEncoding(null!, Encoding.ASCII));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ChangeEncoding_ShouldThrow_GivenNullDestinationEncoding()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => "Hello World".ChangeEncoding(Encoding.UTF8, null!));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnumParse_ShouldReturnCorrectValue_GivenString()
|
||||
{
|
||||
Assert.AreEqual(DayOfWeek.Monday, "Monday".EnumParse<DayOfWeek>(false));
|
||||
Assert.AreEqual(DayOfWeek.Tuesday, "Tuesday".EnumParse<DayOfWeek>(false));
|
||||
Assert.AreEqual(DayOfWeek.Wednesday, "Wednesday".EnumParse<DayOfWeek>(false));
|
||||
Assert.AreEqual(DayOfWeek.Thursday, "Thursday".EnumParse<DayOfWeek>(false));
|
||||
Assert.AreEqual(DayOfWeek.Friday, "Friday".EnumParse<DayOfWeek>(false));
|
||||
Assert.AreEqual(DayOfWeek.Saturday, "Saturday".EnumParse<DayOfWeek>(false));
|
||||
Assert.AreEqual(DayOfWeek.Sunday, "Sunday".EnumParse<DayOfWeek>(false));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnumParse_ShouldTrim()
|
||||
{
|
||||
Assert.AreEqual(DayOfWeek.Monday, " Monday ".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Tuesday, " Tuesday ".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Wednesday, " Wednesday ".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Thursday, " Thursday ".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Friday, " Friday ".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Saturday, " Saturday ".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Sunday, " Sunday ".EnumParse<DayOfWeek>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnumParse_ShouldReturnCorrectValue_GivenString_Generic()
|
||||
{
|
||||
Assert.AreEqual(DayOfWeek.Monday, "Monday".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Tuesday, "Tuesday".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Wednesday, "Wednesday".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Thursday, "Thursday".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Friday, "Friday".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Saturday, "Saturday".EnumParse<DayOfWeek>());
|
||||
Assert.AreEqual(DayOfWeek.Sunday, "Sunday".EnumParse<DayOfWeek>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnumParse_ShouldThrow_GivenNullString()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.EnumParse<DayOfWeek>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnumParse_ShouldThrow_GivenEmptyOrWhiteSpaceString()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => string.Empty.EnumParse<DayOfWeek>());
|
||||
Assert.ThrowsException<ArgumentException>(() => " ".EnumParse<DayOfWeek>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FromJson_ShouldDeserializeCorrectly_GivenJsonString()
|
||||
{
|
||||
const string json = "{\"values\": [1, 2, 3]}";
|
||||
var target = json.FromJson<SampleStructure>();
|
||||
@ -60,21 +164,64 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsLowerShouldBeCorrect()
|
||||
public void GetBytes_ShouldReturnUtf8Bytes_GivenHelloWorld()
|
||||
{
|
||||
var expected = new byte[] {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64};
|
||||
byte[] actual = "Hello World".GetBytes();
|
||||
|
||||
CollectionAssert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetBytes_ShouldReturnAsciiBytes_GivenHelloWorld()
|
||||
{
|
||||
var expected = new byte[] {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64};
|
||||
byte[] actual = "Hello World".GetBytes(Encoding.ASCII);
|
||||
|
||||
CollectionAssert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetBytes_ShouldThrow_GivenNullString()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.GetBytes());
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.GetBytes(Encoding.ASCII));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetBytes_ShouldThrow_GivenNullEncoding()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => "Hello World".GetBytes(null!));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsLower_ShouldReturnTrue_GivenLowercaseString()
|
||||
{
|
||||
Assert.IsTrue("hello world".IsLower());
|
||||
Assert.IsFalse("HELLO WORLD".IsLower());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsLower_ShouldReturnFalse_GivenMixedCaseString()
|
||||
{
|
||||
Assert.IsFalse("Hello World".IsLower());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsLowerNullShouldThrow()
|
||||
public void IsLower_ShouldReturnFalse_GivenUppercaseString()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.IsLower());
|
||||
Assert.IsFalse("HELLO WORLD".IsLower());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsPalindromeShouldBeCorrect()
|
||||
public void IsLower_ShouldThrow_GivenNull()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.IsLower());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsPalindrome_ShouldBeCorrect_GivenString()
|
||||
{
|
||||
const string inputA = "Race car";
|
||||
const string inputB = "Racecar";
|
||||
@ -92,33 +239,71 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsPalindromeEmptyShouldBeFalse()
|
||||
public void IsPalindrome_ShouldReturnFalse_GivenEmptyString()
|
||||
{
|
||||
Assert.IsFalse(string.Empty.IsPalindrome());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsPalindromeNullShouldThrow()
|
||||
public void IsPalindrome_ShouldThrow_GivenNull()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.IsPalindrome());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsUpperShouldBeCorrect()
|
||||
public void IsUpper_ShouldReturnFalse_GivenLowercaseString()
|
||||
{
|
||||
Assert.IsTrue("HELLO WORLD".IsUpper());
|
||||
Assert.IsFalse("hello world".IsUpper());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsUpper_ShouldReturnFalse_GivenMixedCaseString()
|
||||
{
|
||||
Assert.IsFalse("Hello World".IsUpper());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsUpperNullShouldThrow()
|
||||
public void IsUpper_ShouldReturnTrue_GivenUppercaseString()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.IsUpper());
|
||||
Assert.IsTrue("HELLO WORLD".IsUpper());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RepeatShouldBeCorrect()
|
||||
public void IsUpper_ShouldThrow_GivenNull()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.IsUpper());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Randomize_ShouldReorder_GivenString()
|
||||
{
|
||||
const string input = "Hello World";
|
||||
var random = new Random(1);
|
||||
Assert.AreEqual("le rooldeoH", input.Randomize(input.Length, random));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Randomize_ShouldReturnEmptyString_GivenLength1()
|
||||
{
|
||||
Assert.AreEqual(string.Empty, "Hello World".Randomize(0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Randomize_ShouldThrow_GivenNull()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.Randomize(1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Randomize_ShouldThrow_GivenNegativeLength()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => string.Empty.Randomize(-1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Repeat_ShouldReturnRepeatedString_GivenString()
|
||||
{
|
||||
const string expected = "aaaaaaaaaa";
|
||||
string actual = "a".Repeat(10);
|
||||
@ -127,7 +312,13 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RepeatOneCountShouldBeLength1String()
|
||||
public void Repeat_ShouldReturnEmptyString_GivenCount0()
|
||||
{
|
||||
Assert.AreEqual(string.Empty, "a".Repeat(0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Repeat_ShouldReturnItself_GivenCount1()
|
||||
{
|
||||
string repeated = "a".Repeat(1);
|
||||
Assert.AreEqual(1, repeated.Length);
|
||||
@ -135,25 +326,20 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RepeatZeroCountShouldBeEmpty()
|
||||
{
|
||||
Assert.AreEqual(string.Empty, "a".Repeat(0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RepeatNegativeCountShouldThrow()
|
||||
public void Repeat_ShouldThrow_GivenNegativeCount()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => "a".Repeat(-1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RepeatNullShouldThrow()
|
||||
public void Repeat_ShouldThrow_GivenNull()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.Repeat(0));
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.Repeat(0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReverseShouldBeCorrect()
|
||||
public void Reverse_ShouldBeCorrect()
|
||||
{
|
||||
const string input = "Hello World";
|
||||
const string expected = "dlroW olleH";
|
||||
@ -166,13 +352,14 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReverseNullShouldThrow()
|
||||
public void Reverse_ShouldThrow_GivenNull()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.Reverse());
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.Reverse());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShuffleShouldReorder()
|
||||
public void Shuffled_ShouldReorder_GivenString()
|
||||
{
|
||||
const string alphabet = "abcdefghijklmnopqrstuvwxyz";
|
||||
string shuffled = alphabet;
|
||||
@ -185,13 +372,44 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NullShuffleShouldThrow()
|
||||
public void Shuffled_ShouldThrow_GivenNull()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((string?)null)!.Shuffled());
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.Shuffled());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WithEmptyAlternativeShouldBeCorrect()
|
||||
public void Split_ShouldYieldCorrectStrings_GivenString()
|
||||
{
|
||||
string[] chunks = "Hello World".Split(2).ToArray();
|
||||
Assert.AreEqual(6, chunks.Length);
|
||||
Assert.AreEqual("He", chunks[0]);
|
||||
Assert.AreEqual("ll", chunks[1]);
|
||||
Assert.AreEqual("o ", chunks[2]);
|
||||
Assert.AreEqual("Wo", chunks[3]);
|
||||
Assert.AreEqual("rl", chunks[4]);
|
||||
Assert.AreEqual("d", chunks[5]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Split_ShouldYieldEmptyString_GivenChunkSize0()
|
||||
{
|
||||
string[] chunks = "Hello World".Split(0).ToArray();
|
||||
Assert.AreEqual(1, chunks.Length);
|
||||
Assert.AreEqual(string.Empty, chunks[0]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Split_ShouldThrow_GivenNullString()
|
||||
{
|
||||
string? value = null;
|
||||
|
||||
// forcing enumeration with ToArray is required for the exception to be thrown
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.Split(0).ToArray());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WithEmptyAlternative_ShouldBeCorrect()
|
||||
{
|
||||
const string inputA = "Hello World";
|
||||
const string inputB = " ";
|
||||
@ -212,7 +430,7 @@ public class StringTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WithWhiteSpaceAlternativeShouldBeCorrect()
|
||||
public void WithWhiteSpaceAlternative_ShouldBeCorrect()
|
||||
{
|
||||
const string input = " ";
|
||||
const string alternative = "ALTERNATIVE";
|
||||
|
38
X10D.Tests/src/Time/StringTests.cs
Normal file
38
X10D.Tests/src/Time/StringTests.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Time;
|
||||
|
||||
namespace X10D.Tests.Time;
|
||||
|
||||
[TestClass]
|
||||
public class StringTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ToTimeSpan_ShouldReturnCorrectTimeSpan_GivenString()
|
||||
{
|
||||
const string value = "1y 1mo 1w 1d 1h 1m 1s 1ms";
|
||||
|
||||
TimeSpan expected = TimeSpan.FromMilliseconds(1);
|
||||
expected += TimeSpan.FromSeconds(1);
|
||||
expected += TimeSpan.FromMinutes(1);
|
||||
expected += TimeSpan.FromHours(1);
|
||||
expected += TimeSpan.FromDays(1);
|
||||
expected += TimeSpan.FromDays(7);
|
||||
expected += TimeSpan.FromDays(30);
|
||||
expected += TimeSpan.FromDays(365);
|
||||
|
||||
Assert.AreEqual(expected, value.ToTimeSpan());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToTimeSpan_ShouldReturnZero_GivenInvalidString()
|
||||
{
|
||||
Assert.AreEqual(TimeSpan.Zero, "Hello World".ToTimeSpan());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToTimeSpan_ShouldThrow_GivenNullString()
|
||||
{
|
||||
string? value = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => value!.ToTimeSpan());
|
||||
}
|
||||
}
|
@ -1,266 +0,0 @@
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using X10D.Core;
|
||||
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="string" />.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent plain text string.
|
||||
/// </summary>
|
||||
/// <param name="value">The base-64 string to convert.</param>
|
||||
/// <returns>The plain text string representation of <paramref name="value" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string Base64Decode(this string value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
return Convert.FromBase64String(value).ToString(Encoding.ASCII);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the current string to its equivalent string representation that is encoded with base-64 digits.
|
||||
/// </summary>
|
||||
/// <param name="value">The plain text string to convert.</param>
|
||||
/// <returns>The string representation, in base 64, of <paramref name="value" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string Base64Encode(this string value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(value.GetBytes(Encoding.ASCII));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this string from one encoding to another.
|
||||
/// </summary>
|
||||
/// <param name="value">The input string.</param>
|
||||
/// <param name="sourceEncoding">The input encoding.</param>
|
||||
/// <param name="destinationEncoding">The output encoding.</param>
|
||||
/// <returns>
|
||||
/// Returns a new <see cref="string" /> with its data converted to
|
||||
/// <paramref name="destinationEncoding" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="value" /> is <see langword="null" />
|
||||
/// - or -
|
||||
/// <paramref name="sourceEncoding" /> is <see langword="null" />
|
||||
/// -or
|
||||
/// <paramref name="destinationEncoding" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string ChangeEncoding(this string value, Encoding sourceEncoding, Encoding destinationEncoding)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (sourceEncoding is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceEncoding));
|
||||
}
|
||||
|
||||
if (destinationEncoding is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(destinationEncoding));
|
||||
}
|
||||
|
||||
return value.GetBytes(sourceEncoding).ToString(destinationEncoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a <see cref="string" /> into an <see cref="Enum" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the <see cref="Enum" />.</typeparam>
|
||||
/// <param name="value">The <see cref="string" /> value to parse.</param>
|
||||
/// <returns>The <see cref="Enum" /> value corresponding to the <see cref="string" />.</returns>
|
||||
/// <remarks>
|
||||
/// Credit for this method goes to Scott Dorman:
|
||||
/// (http://geekswithblogs.net/sdorman/Default.aspx).
|
||||
/// </remarks>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static T EnumParse<T>(this string value)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return value.EnumParse<T>(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a <see cref="string" /> into an <see cref="Enum" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the <see cref="Enum" />.</typeparam>
|
||||
/// <param name="value">The <see cref="string" /> value to parse.</param>
|
||||
/// <param name="ignoreCase">Whether or not to ignore casing.</param>
|
||||
/// <returns>The <see cref="Enum" /> value corresponding to the <see cref="string" />.</returns>
|
||||
/// <remarks>
|
||||
/// Credit for this method goes to Scott Dorman:
|
||||
/// (http://geekswithblogs.net/sdorman/Default.aspx).
|
||||
/// </remarks>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static T EnumParse<T>(this string value, bool ignoreCase)
|
||||
where T : struct, Enum
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
value = value.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentException(Resource.EnumParseEmptyStringException, nameof(value));
|
||||
}
|
||||
|
||||
return Enum.Parse<T>(value, ignoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="byte" />[] representing the value the <see cref="string" /> with
|
||||
/// <see cref="Encoding.UTF8" /> encoding.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to convert.</param>
|
||||
/// <returns>Returns a <see cref="byte" />[].</returns>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static byte[] GetBytes(this string value)
|
||||
{
|
||||
return value.GetBytes(Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="byte" />[] representing the value the <see cref="string" /> with the provided encoding.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to convert.</param>
|
||||
/// <param name="encoding">The encoding to use.</param>
|
||||
/// <returns>Returns a <see cref="byte" />[].</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="value" /> or <paramref name="encoding" /> or both are
|
||||
/// <see langword="null" />.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static byte[] GetBytes(this string value, Encoding encoding)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (encoding is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(encoding));
|
||||
}
|
||||
|
||||
return encoding.GetBytes(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new string of a specified length by randomly selecting characters from the current string.
|
||||
/// </summary>
|
||||
/// <param name="source">The pool of characters to use.</param>
|
||||
/// <param name="length">The length of the new string returned.</param>
|
||||
/// <param name="random">The <see cref="System.Random" /> supplier.</param>
|
||||
/// <returns>
|
||||
/// A new string whose length is equal to <paramref name="length" /> which contains randomly selected characters from
|
||||
/// <paramref name="source" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="length" /> is less than 0.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string Randomize(this string source, int length, Random? random = null)
|
||||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (length < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length), ExceptionMessages.LengthGreaterThanOrEqualTo0);
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
random ??= Random.Shared;
|
||||
|
||||
char[] array = source.ToCharArray();
|
||||
var builder = new StringBuilder(length);
|
||||
|
||||
while (builder.Length < length)
|
||||
{
|
||||
char next = random.NextFrom(array);
|
||||
builder.Append(next);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the <see cref="string" /> into chunks that are no greater than <paramref name="chunkSize" /> in length.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to split.</param>
|
||||
/// <param name="chunkSize">The maximum length of each string in the returned result.</param>
|
||||
/// <returns>
|
||||
/// Returns an <see cref="IEnumerable{T}" /> containing <see cref="string" /> instances which are no
|
||||
/// greater than <paramref name="chunkSize" /> in length.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static IEnumerable<string> Split(this string value, int chunkSize)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
for (var i = 0; i < value.Length; i += chunkSize)
|
||||
{
|
||||
yield return value[i..System.Math.Min(i + chunkSize, value.Length - 1)];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a shorthand time span string (e.g. 3w 2d 1.5h) and converts it to an instance of <see cref="TimeSpan" />.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
/// <returns>Returns an instance of <see cref="TimeSpan" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="input" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static TimeSpan ToTimeSpan(this string input)
|
||||
{
|
||||
if (input is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
return TimeSpanParser.TryParse(input, out TimeSpan result)
|
||||
? result
|
||||
: default;
|
||||
}
|
||||
}
|
@ -189,7 +189,14 @@ public class StringBuilderReader : TextReader
|
||||
_index++;
|
||||
}
|
||||
|
||||
return _stringBuilder.ToString(start, _index - start - 1);
|
||||
var result = _stringBuilder.ToString(start, _index - start);
|
||||
|
||||
if (result.Length > 0 && (result[^1] == '\n' || result[^1] == '\r'))
|
||||
{
|
||||
result = result[..^1];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -4,6 +4,8 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using X10D.Collections;
|
||||
using X10D.Core;
|
||||
using X10D.IO;
|
||||
|
||||
namespace X10D.Text;
|
||||
|
||||
@ -45,6 +47,130 @@ public static class StringExtensions
|
||||
return value.WithWhiteSpaceAlternative(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent plain text string.
|
||||
/// </summary>
|
||||
/// <param name="value">The base-64 string to convert.</param>
|
||||
/// <returns>The plain text string representation of <paramref name="value" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string Base64Decode(this string value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
return Convert.FromBase64String(value).ToString(Encoding.ASCII);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the current string to its equivalent string representation that is encoded with base-64 digits.
|
||||
/// </summary>
|
||||
/// <param name="value">The plain text string to convert.</param>
|
||||
/// <returns>The string representation, in base 64, of <paramref name="value" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string Base64Encode(this string value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(value.GetBytes(Encoding.ASCII));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this string from one encoding to another.
|
||||
/// </summary>
|
||||
/// <param name="value">The input string.</param>
|
||||
/// <param name="sourceEncoding">The input encoding.</param>
|
||||
/// <param name="destinationEncoding">The output encoding.</param>
|
||||
/// <returns>
|
||||
/// Returns a new <see cref="string" /> with its data converted to
|
||||
/// <paramref name="destinationEncoding" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="value" /> is <see langword="null" />
|
||||
/// - or -
|
||||
/// <paramref name="sourceEncoding" /> is <see langword="null" />
|
||||
/// -or
|
||||
/// <paramref name="destinationEncoding" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string ChangeEncoding(this string value, Encoding sourceEncoding, Encoding destinationEncoding)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (sourceEncoding is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceEncoding));
|
||||
}
|
||||
|
||||
if (destinationEncoding is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(destinationEncoding));
|
||||
}
|
||||
|
||||
return value.GetBytes(sourceEncoding).ToString(destinationEncoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a <see cref="string" /> into an <see cref="Enum" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the <see cref="Enum" />.</typeparam>
|
||||
/// <param name="value">The <see cref="string" /> value to parse.</param>
|
||||
/// <returns>The <see cref="Enum" /> value corresponding to the <see cref="string" />.</returns>
|
||||
/// <remarks>
|
||||
/// Credit for this method goes to Scott Dorman:
|
||||
/// (http://geekswithblogs.net/sdorman/Default.aspx).
|
||||
/// </remarks>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static T EnumParse<T>(this string value)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return value.EnumParse<T>(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a <see cref="string" /> into an <see cref="Enum" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the <see cref="Enum" />.</typeparam>
|
||||
/// <param name="value">The <see cref="string" /> value to parse.</param>
|
||||
/// <param name="ignoreCase">Whether or not to ignore casing.</param>
|
||||
/// <returns>The <see cref="Enum" /> value corresponding to the <see cref="string" />.</returns>
|
||||
/// <remarks>
|
||||
/// Credit for this method goes to Scott Dorman:
|
||||
/// (http://geekswithblogs.net/sdorman/Default.aspx).
|
||||
/// </remarks>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static T EnumParse<T>(this string value, bool ignoreCase)
|
||||
where T : struct, Enum
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
value = value.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentException(Resource.EnumParseEmptyStringException, nameof(value));
|
||||
}
|
||||
|
||||
return Enum.Parse<T>(value, ignoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object from the specified JSON string.
|
||||
/// </summary>
|
||||
@ -61,6 +187,46 @@ public static class StringExtensions
|
||||
return JsonSerializer.Deserialize<T>(value, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="byte" />[] representing the value the <see cref="string" /> with
|
||||
/// <see cref="Encoding.UTF8" /> encoding.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to convert.</param>
|
||||
/// <returns>Returns a <see cref="byte" />[].</returns>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static byte[] GetBytes(this string value)
|
||||
{
|
||||
return value.GetBytes(Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="byte" />[] representing the value the <see cref="string" /> with the provided encoding.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to convert.</param>
|
||||
/// <param name="encoding">The encoding to use.</param>
|
||||
/// <returns>Returns a <see cref="byte" />[].</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="value" /> or <paramref name="encoding" /> or both are
|
||||
/// <see langword="null" />.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static byte[] GetBytes(this string value, Encoding encoding)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (encoding is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(encoding));
|
||||
}
|
||||
|
||||
return encoding.GetBytes(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if all alpha characters in this string are considered lowercase.
|
||||
/// </summary>
|
||||
@ -217,6 +383,51 @@ public static class StringExtensions
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new string of a specified length by randomly selecting characters from the current string.
|
||||
/// </summary>
|
||||
/// <param name="source">The pool of characters to use.</param>
|
||||
/// <param name="length">The length of the new string returned.</param>
|
||||
/// <param name="random">The <see cref="System.Random" /> supplier.</param>
|
||||
/// <returns>
|
||||
/// A new string whose length is equal to <paramref name="length" /> which contains randomly selected characters from
|
||||
/// <paramref name="source" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="length" /> is less than 0.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static string Randomize(this string source, int length, Random? random = null)
|
||||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (length < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length), ExceptionMessages.LengthGreaterThanOrEqualTo0);
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
random ??= Random.Shared;
|
||||
|
||||
char[] array = source.ToCharArray();
|
||||
var builder = new StringBuilder(length);
|
||||
|
||||
while (builder.Length < length)
|
||||
{
|
||||
char next = random.NextFrom(array);
|
||||
builder.Append(next);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the current string.
|
||||
/// </summary>
|
||||
@ -272,6 +483,37 @@ public static class StringExtensions
|
||||
return new string(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the <see cref="string" /> into chunks that are no greater than <paramref name="chunkSize" /> in length.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to split.</param>
|
||||
/// <param name="chunkSize">The maximum length of each string in the returned result.</param>
|
||||
/// <returns>
|
||||
/// Returns an <see cref="IEnumerable{T}" /> containing <see cref="string" /> instances which are no
|
||||
/// greater than <paramref name="chunkSize" /> in length.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static IEnumerable<string> Split(this string value, int chunkSize)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (chunkSize == 0)
|
||||
{
|
||||
yield return string.Empty;
|
||||
yield break;
|
||||
}
|
||||
|
||||
for (var i = 0; i < value.Length; i += chunkSize)
|
||||
{
|
||||
yield return value[i..System.Math.Min(i + chunkSize, value.Length)];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a string which may be either <see langword="null" /> or empty to a specified alternative.
|
||||
/// </summary>
|
||||
|
72
X10D/src/Time/StringExtensions.cs
Normal file
72
X10D/src/Time/StringExtensions.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace X10D.Time;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="string" />.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a shorthand time span string (e.g. 3w 2d 1h) and converts it to an instance of <see cref="TimeSpan" />.
|
||||
/// </summary>
|
||||
/// <param name="input">
|
||||
/// The input string. Floating point is not supported, but range the following units are supported:
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Suffix</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>ms</term>
|
||||
/// <description>Milliseconds</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>s</term>
|
||||
/// <description>Seconds</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>m</term>
|
||||
/// <description>Minutes</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>h</term>
|
||||
/// <description>Hours</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>d</term>
|
||||
/// <description>Days</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>w</term>
|
||||
/// <description>Weeks</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>mo</term>
|
||||
/// <description>Months</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>y</term>
|
||||
/// <description>Years</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </param>
|
||||
/// <returns>A new instance of <see cref="TimeSpan" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="input" /> is <see langword="null" />.</exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static TimeSpan ToTimeSpan(this string input)
|
||||
{
|
||||
if (input is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
return TimeSpanParser.TryParse(input, out TimeSpan result)
|
||||
? result
|
||||
: default;
|
||||
}
|
||||
}
|
133
X10D/src/Time/TimeSpanParser.cs
Normal file
133
X10D/src/Time/TimeSpanParser.cs
Normal file
@ -0,0 +1,133 @@
|
||||
namespace X10D.Time;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a class which contains a <see cref="string" /> parser which converts into <see cref="TimeSpan" />.
|
||||
/// </summary>
|
||||
public static class TimeSpanParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to parses a shorthand time span string (e.g. 3w 2d 1h), converting it to an instance of
|
||||
/// <see cref="TimeSpan" /> which represents that duration of time.
|
||||
/// </summary>
|
||||
/// <param name="value">
|
||||
/// The input string. Floating point is not supported, but range the following units are supported:
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Suffix</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>ms</term>
|
||||
/// <description>Milliseconds</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>s</term>
|
||||
/// <description>Seconds</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>m</term>
|
||||
/// <description>Minutes</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>h</term>
|
||||
/// <description>Hours</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>d</term>
|
||||
/// <description>Days</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>w</term>
|
||||
/// <description>Weeks</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>mo</term>
|
||||
/// <description>Months</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>y</term>
|
||||
/// <description>Years</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </param>
|
||||
/// <param name="result">When this method returns, contains the parsed result.</param>
|
||||
/// <returns><see langword="true" /> if the parse was successful, <see langword="false" /> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
public static bool TryParse(string value, out TimeSpan result)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
result = TimeSpan.Zero;
|
||||
var unitValue = 0;
|
||||
|
||||
for (var index = 0; index < value.Length; index++)
|
||||
{
|
||||
char current = value[index];
|
||||
switch (current)
|
||||
{
|
||||
case var digitChar when char.IsDigit(digitChar):
|
||||
var digit = (int)char.GetNumericValue(digitChar);
|
||||
unitValue = unitValue * 10 + digit;
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
result += TimeSpan.FromDays(unitValue * 365);
|
||||
unitValue = 0;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
if (index < value.Length - 1 && value[index + 1] == 'o')
|
||||
{
|
||||
index++;
|
||||
result += TimeSpan.FromDays(unitValue * 30);
|
||||
}
|
||||
else if (index < value.Length - 1 && value[index + 1] == 's')
|
||||
{
|
||||
index++;
|
||||
result += TimeSpan.FromMilliseconds(unitValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
result += TimeSpan.FromMinutes(unitValue);
|
||||
}
|
||||
|
||||
unitValue = 0;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
result += TimeSpan.FromDays(unitValue * 7);
|
||||
unitValue = 0;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
result += TimeSpan.FromDays(unitValue);
|
||||
unitValue = 0;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
result += TimeSpan.FromHours(unitValue);
|
||||
unitValue = 0;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
result += TimeSpan.FromSeconds(unitValue);
|
||||
unitValue = 0;
|
||||
break;
|
||||
|
||||
case var space when char.IsWhiteSpace(space):
|
||||
break;
|
||||
|
||||
default:
|
||||
result = TimeSpan.Zero;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a class which contains a <see cref="string" /> parser which converts into <see cref="TimeSpan" />.
|
||||
/// </summary>
|
||||
public static class TimeSpanParser
|
||||
{
|
||||
private const string RealNumberPattern = @"(\d*\.\d+|\d+)";
|
||||
|
||||
private static readonly string Pattern = $"^(?:{RealNumberPattern} *y)? *" +
|
||||
$"^(?:{RealNumberPattern} *mo)? *" +
|
||||
$"^(?:{RealNumberPattern} *w)? *" +
|
||||
$"(?:{RealNumberPattern} *d)? *" +
|
||||
$"(?:{RealNumberPattern} *h)? *" +
|
||||
$"(?:{RealNumberPattern} *m)? *" +
|
||||
$"(?:{RealNumberPattern} *s)? *" +
|
||||
$"(?:{RealNumberPattern} *ms)?$";
|
||||
|
||||
private static readonly Regex Regex = new(Pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parses a shorthand time span string (e.g. 3w 2d 1.5h), converting it to an instance of
|
||||
/// <see cref="TimeSpan" /> which represents that duration of time.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
/// <param name="result">The parsed result.</param>
|
||||
/// <param name="provider">The format provider.</param>
|
||||
/// <returns><see langword="true" /> if the parse was successful, <see langword="false" /> otherwise.</returns>
|
||||
public static bool TryParse(string input, out TimeSpan result, IFormatProvider? provider = null)
|
||||
{
|
||||
result = default;
|
||||
|
||||
Match? match = Regex.Match(input);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryParseAt(int group, out double parsedResult)
|
||||
{
|
||||
parsedResult = 0;
|
||||
|
||||
return match.Groups[group].Success
|
||||
&& double.TryParse(match.Groups[group].Value, NumberStyles.Number, provider, out parsedResult);
|
||||
}
|
||||
|
||||
if (!TryParseAt(1, out double years))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseAt(2, out double months))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseAt(3, out double weeks))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseAt(4, out double days))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseAt(5, out double hours))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseAt(6, out double minutes))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseAt(7, out double seconds))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseAt(8, out double milliseconds))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result += TimeSpan.FromDays(years * 365);
|
||||
result += TimeSpan.FromDays(months * 30);
|
||||
result += TimeSpan.FromDays(weeks * 7);
|
||||
result += TimeSpan.FromDays(days);
|
||||
result += TimeSpan.FromHours(hours);
|
||||
result += TimeSpan.FromMinutes(minutes);
|
||||
result += TimeSpan.FromSeconds(seconds);
|
||||
result += TimeSpan.FromMilliseconds(milliseconds);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user