diff --git a/X10D.Tests/src/Core/StringTests.cs b/X10D.Tests/src/Core/StringTests.cs
deleted file mode 100644
index ee6d169..0000000
--- a/X10D.Tests/src/Core/StringTests.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace X10D.Tests.Core;
-
-///
-/// Tests for .
-///
-[TestClass]
-public class StringTests
-{
- ///
- /// Tests .
- ///
- [TestMethod]
- public void Base64Decode()
- {
- const string input = "SGVsbG8gV29ybGQ=";
- const string expected = "Hello World";
-
- string result = input.Base64Decode();
-
- Assert.AreEqual(expected, result);
- }
-
-
- ///
- /// Tests .
- ///
- [TestMethod]
- public void Base64Encode()
- {
- const string input = "Hello World";
- const string expected = "SGVsbG8gV29ybGQ=";
-
- string result = input.Base64Encode();
-
- Assert.AreEqual(expected, result);
- }
-
- ///
- /// Tests .
- ///
- [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(() => ((string?)null)!.Randomize(1));
- Assert.ThrowsException(() => input.Randomize(-1));
- Assert.AreEqual(string.Empty, string.Empty.Randomize(0));
- Assert.AreEqual(expected, result);
- }
-}
diff --git a/X10D.Tests/src/Text/StringBuilderReaderTests.cs b/X10D.Tests/src/Text/StringBuilderReaderTests.cs
new file mode 100644
index 0000000..bdd0848
--- /dev/null
+++ b/X10D.Tests/src/Text/StringBuilderReaderTests.cs
@@ -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(() =>
+ {
+ using var reader = new StringBuilderReader(new StringBuilder("Hello\nWorld"));
+ reader.Read(null!, 0, 5);
+ reader.Close();
+ });
+ }
+
+ [TestMethod]
+ public void Read_ShouldThrow_GivenNegativeIndex()
+ {
+ Assert.ThrowsException(() =>
+ {
+ 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(() =>
+ {
+ 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(() =>
+ {
+ 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 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 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 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 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();
+ }
+}
diff --git a/X10D.Tests/src/Text/StringTests.cs b/X10D.Tests/src/Text/StringTests.cs
index 6e13ab5..5d509f0 100644
--- a/X10D.Tests/src/Text/StringTests.cs
+++ b/X10D.Tests/src/Text/StringTests.cs
@@ -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(() => 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(() => 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(() => value!.ChangeEncoding(Encoding.UTF8, Encoding.ASCII));
+ }
+
+ [TestMethod]
+ public void ChangeEncoding_ShouldThrow_GivenNullSourceEncoding()
+ {
+ Assert.ThrowsException(() => "Hello World".ChangeEncoding(null!, Encoding.ASCII));
+ }
+
+ [TestMethod]
+ public void ChangeEncoding_ShouldThrow_GivenNullDestinationEncoding()
+ {
+ Assert.ThrowsException(() => "Hello World".ChangeEncoding(Encoding.UTF8, null!));
+ }
+
+ [TestMethod]
+ public void EnumParse_ShouldReturnCorrectValue_GivenString()
+ {
+ Assert.AreEqual(DayOfWeek.Monday, "Monday".EnumParse(false));
+ Assert.AreEqual(DayOfWeek.Tuesday, "Tuesday".EnumParse(false));
+ Assert.AreEqual(DayOfWeek.Wednesday, "Wednesday".EnumParse(false));
+ Assert.AreEqual(DayOfWeek.Thursday, "Thursday".EnumParse(false));
+ Assert.AreEqual(DayOfWeek.Friday, "Friday".EnumParse(false));
+ Assert.AreEqual(DayOfWeek.Saturday, "Saturday".EnumParse(false));
+ Assert.AreEqual(DayOfWeek.Sunday, "Sunday".EnumParse(false));
+ }
+
+ [TestMethod]
+ public void EnumParse_ShouldTrim()
+ {
+ Assert.AreEqual(DayOfWeek.Monday, " Monday ".EnumParse());
+ Assert.AreEqual(DayOfWeek.Tuesday, " Tuesday ".EnumParse());
+ Assert.AreEqual(DayOfWeek.Wednesday, " Wednesday ".EnumParse());
+ Assert.AreEqual(DayOfWeek.Thursday, " Thursday ".EnumParse());
+ Assert.AreEqual(DayOfWeek.Friday, " Friday ".EnumParse());
+ Assert.AreEqual(DayOfWeek.Saturday, " Saturday ".EnumParse());
+ Assert.AreEqual(DayOfWeek.Sunday, " Sunday ".EnumParse());
+ }
+
+ [TestMethod]
+ public void EnumParse_ShouldReturnCorrectValue_GivenString_Generic()
+ {
+ Assert.AreEqual(DayOfWeek.Monday, "Monday".EnumParse());
+ Assert.AreEqual(DayOfWeek.Tuesday, "Tuesday".EnumParse());
+ Assert.AreEqual(DayOfWeek.Wednesday, "Wednesday".EnumParse());
+ Assert.AreEqual(DayOfWeek.Thursday, "Thursday".EnumParse());
+ Assert.AreEqual(DayOfWeek.Friday, "Friday".EnumParse());
+ Assert.AreEqual(DayOfWeek.Saturday, "Saturday".EnumParse());
+ Assert.AreEqual(DayOfWeek.Sunday, "Sunday".EnumParse());
+ }
+
+ [TestMethod]
+ public void EnumParse_ShouldThrow_GivenNullString()
+ {
+ string? value = null;
+ Assert.ThrowsException(() => value!.EnumParse());
+ }
+
+ [TestMethod]
+ public void EnumParse_ShouldThrow_GivenEmptyOrWhiteSpaceString()
+ {
+ Assert.ThrowsException(() => string.Empty.EnumParse());
+ Assert.ThrowsException(() => " ".EnumParse());
+ }
+
+ [TestMethod]
+ public void FromJson_ShouldDeserializeCorrectly_GivenJsonString()
{
const string json = "{\"values\": [1, 2, 3]}";
var target = json.FromJson();
@@ -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(() => value!.GetBytes());
+ Assert.ThrowsException(() => value!.GetBytes(Encoding.ASCII));
+ }
+
+ [TestMethod]
+ public void GetBytes_ShouldThrow_GivenNullEncoding()
+ {
+ Assert.ThrowsException(() => "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(() => ((string?)null)!.IsLower());
+ Assert.IsFalse("HELLO WORLD".IsLower());
}
[TestMethod]
- public void IsPalindromeShouldBeCorrect()
+ public void IsLower_ShouldThrow_GivenNull()
+ {
+ string? value = null;
+ Assert.ThrowsException(() => 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(() => ((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(() => ((string?)null)!.IsUpper());
+ Assert.IsTrue("HELLO WORLD".IsUpper());
}
[TestMethod]
- public void RepeatShouldBeCorrect()
+ public void IsUpper_ShouldThrow_GivenNull()
+ {
+ string? value = null;
+ Assert.ThrowsException(() => 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(() => value!.Randomize(1));
+ }
+
+ [TestMethod]
+ public void Randomize_ShouldThrow_GivenNegativeLength()
+ {
+ Assert.ThrowsException(() => 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(() => "a".Repeat(-1));
}
[TestMethod]
- public void RepeatNullShouldThrow()
+ public void Repeat_ShouldThrow_GivenNull()
{
- Assert.ThrowsException(() => ((string?)null)!.Repeat(0));
+ string? value = null;
+ Assert.ThrowsException(() => 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(() => ((string?)null)!.Reverse());
+ string? value = null;
+ Assert.ThrowsException(() => 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(() => ((string?)null)!.Shuffled());
+ string? value = null;
+ Assert.ThrowsException(() => 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(() => 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";
diff --git a/X10D.Tests/src/Time/StringTests.cs b/X10D.Tests/src/Time/StringTests.cs
new file mode 100644
index 0000000..9b5e68c
--- /dev/null
+++ b/X10D.Tests/src/Time/StringTests.cs
@@ -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(() => value!.ToTimeSpan());
+ }
+}
diff --git a/X10D/src/StringExtensions/StringExtensions.cs b/X10D/src/StringExtensions/StringExtensions.cs
deleted file mode 100644
index c51a2e8..0000000
--- a/X10D/src/StringExtensions/StringExtensions.cs
+++ /dev/null
@@ -1,266 +0,0 @@
-using System.Diagnostics.Contracts;
-using System.Runtime.CompilerServices;
-using System.Text;
-using X10D.Core;
-
-namespace X10D;
-
-///
-/// Extension methods for .
-///
-public static class StringExtensions
-{
- ///
- /// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent plain text string.
- ///
- /// The base-64 string to convert.
- /// The plain text string representation of .
- /// is .
- [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);
- }
-
- ///
- /// Converts the current string to its equivalent string representation that is encoded with base-64 digits.
- ///
- /// The plain text string to convert.
- /// The string representation, in base 64, of .
- /// is .
- [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));
- }
-
- ///
- /// Converts this string from one encoding to another.
- ///
- /// The input string.
- /// The input encoding.
- /// The output encoding.
- ///
- /// Returns a new with its data converted to
- /// .
- ///
- ///
- /// is
- /// - or -
- /// is
- /// -or
- /// is .
- ///
- [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);
- }
-
- ///
- /// Parses a into an .
- ///
- /// The type of the .
- /// The value to parse.
- /// The value corresponding to the .
- ///
- /// Credit for this method goes to Scott Dorman:
- /// (http://geekswithblogs.net/sdorman/Default.aspx).
- ///
- [Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static T EnumParse(this string value)
- where T : struct, Enum
- {
- return value.EnumParse(false);
- }
-
- ///
- /// Parses a into an .
- ///
- /// The type of the .
- /// The value to parse.
- /// Whether or not to ignore casing.
- /// The value corresponding to the .
- ///
- /// Credit for this method goes to Scott Dorman:
- /// (http://geekswithblogs.net/sdorman/Default.aspx).
- ///
- [Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static T EnumParse(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(value, ignoreCase);
- }
-
- ///
- /// Gets a [] representing the value the with
- /// encoding.
- ///
- /// The string to convert.
- /// Returns a [].
- [Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static byte[] GetBytes(this string value)
- {
- return value.GetBytes(Encoding.UTF8);
- }
-
- ///
- /// Gets a [] representing the value the with the provided encoding.
- ///
- /// The string to convert.
- /// The encoding to use.
- /// Returns a [].
- ///
- /// or or both are
- /// .
- ///
- [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);
- }
-
- ///
- /// Returns a new string of a specified length by randomly selecting characters from the current string.
- ///
- /// The pool of characters to use.
- /// The length of the new string returned.
- /// The supplier.
- ///
- /// A new string whose length is equal to which contains randomly selected characters from
- /// .
- ///
- /// is .
- /// is less than 0.
- [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();
- }
-
- ///
- /// Splits the into chunks that are no greater than in length.
- ///
- /// The string to split.
- /// The maximum length of each string in the returned result.
- ///
- /// Returns an containing instances which are no
- /// greater than in length.
- ///
- /// is .
- [Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static IEnumerable 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)];
- }
- }
-
- ///
- /// Parses a shorthand time span string (e.g. 3w 2d 1.5h) and converts it to an instance of .
- ///
- /// The input string.
- /// Returns an instance of .
- /// is .
- [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;
- }
-}
diff --git a/X10D/src/Text/StringBuilderReader.cs b/X10D/src/Text/StringBuilderReader.cs
index 13f2737..fe6e969 100644
--- a/X10D/src/Text/StringBuilderReader.cs
+++ b/X10D/src/Text/StringBuilderReader.cs
@@ -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;
}
///
diff --git a/X10D/src/Text/StringExtensions.cs b/X10D/src/Text/StringExtensions.cs
index 803cba8..2f83e3c 100644
--- a/X10D/src/Text/StringExtensions.cs
+++ b/X10D/src/Text/StringExtensions.cs
@@ -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);
}
+ ///
+ /// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent plain text string.
+ ///
+ /// The base-64 string to convert.
+ /// The plain text string representation of .
+ /// is .
+ [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);
+ }
+
+ ///
+ /// Converts the current string to its equivalent string representation that is encoded with base-64 digits.
+ ///
+ /// The plain text string to convert.
+ /// The string representation, in base 64, of .
+ /// is .
+ [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));
+ }
+
+ ///
+ /// Converts this string from one encoding to another.
+ ///
+ /// The input string.
+ /// The input encoding.
+ /// The output encoding.
+ ///
+ /// Returns a new with its data converted to
+ /// .
+ ///
+ ///
+ /// is
+ /// - or -
+ /// is
+ /// -or
+ /// is .
+ ///
+ [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);
+ }
+
+ ///
+ /// Parses a into an .
+ ///
+ /// The type of the .
+ /// The value to parse.
+ /// The value corresponding to the .
+ ///
+ /// Credit for this method goes to Scott Dorman:
+ /// (http://geekswithblogs.net/sdorman/Default.aspx).
+ ///
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static T EnumParse(this string value)
+ where T : struct, Enum
+ {
+ return value.EnumParse(false);
+ }
+
+ ///
+ /// Parses a into an .
+ ///
+ /// The type of the .
+ /// The value to parse.
+ /// Whether or not to ignore casing.
+ /// The value corresponding to the .
+ ///
+ /// Credit for this method goes to Scott Dorman:
+ /// (http://geekswithblogs.net/sdorman/Default.aspx).
+ ///
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static T EnumParse(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(value, ignoreCase);
+ }
+
///
/// Returns an object from the specified JSON string.
///
@@ -61,6 +187,46 @@ public static class StringExtensions
return JsonSerializer.Deserialize(value, options);
}
+ ///
+ /// Gets a [] representing the value the with
+ /// encoding.
+ ///
+ /// The string to convert.
+ /// Returns a [].
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static byte[] GetBytes(this string value)
+ {
+ return value.GetBytes(Encoding.UTF8);
+ }
+
+ ///
+ /// Gets a [] representing the value the with the provided encoding.
+ ///
+ /// The string to convert.
+ /// The encoding to use.
+ /// Returns a [].
+ ///
+ /// or or both are
+ /// .
+ ///
+ [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);
+ }
+
///
/// Determines if all alpha characters in this string are considered lowercase.
///
@@ -217,6 +383,51 @@ public static class StringExtensions
return builder.ToString();
}
+ ///
+ /// Returns a new string of a specified length by randomly selecting characters from the current string.
+ ///
+ /// The pool of characters to use.
+ /// The length of the new string returned.
+ /// The supplier.
+ ///
+ /// A new string whose length is equal to which contains randomly selected characters from
+ /// .
+ ///
+ /// is .
+ /// is less than 0.
+ [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();
+ }
+
///
/// Reverses the current string.
///
@@ -272,6 +483,37 @@ public static class StringExtensions
return new string(array);
}
+ ///
+ /// Splits the into chunks that are no greater than in length.
+ ///
+ /// The string to split.
+ /// The maximum length of each string in the returned result.
+ ///
+ /// Returns an containing instances which are no
+ /// greater than in length.
+ ///
+ /// is .
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static IEnumerable 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)];
+ }
+ }
+
///
/// Normalizes a string which may be either or empty to a specified alternative.
///
diff --git a/X10D/src/Time/StringExtensions.cs b/X10D/src/Time/StringExtensions.cs
new file mode 100644
index 0000000..6ee6afd
--- /dev/null
+++ b/X10D/src/Time/StringExtensions.cs
@@ -0,0 +1,72 @@
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+
+namespace X10D.Time;
+
+///
+/// Extension methods for .
+///
+public static class StringExtensions
+{
+ ///
+ /// Parses a shorthand time span string (e.g. 3w 2d 1h) and converts it to an instance of .
+ ///
+ ///
+ /// The input string. Floating point is not supported, but range the following units are supported:
+ ///
+ ///
+ ///
+ /// Suffix
+ /// Meaning
+ ///
+ ///
+ /// -
+ /// ms
+ /// Milliseconds
+ ///
+ /// -
+ /// s
+ /// Seconds
+ ///
+ /// -
+ /// m
+ /// Minutes
+ ///
+ /// -
+ /// h
+ /// Hours
+ ///
+ /// -
+ /// d
+ /// Days
+ ///
+ /// -
+ /// w
+ /// Weeks
+ ///
+ /// -
+ /// mo
+ /// Months
+ ///
+ /// -
+ /// y
+ /// Years
+ ///
+ ///
+ ///
+ /// A new instance of .
+ /// is .
+ [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;
+ }
+}
diff --git a/X10D/src/Time/TimeSpanParser.cs b/X10D/src/Time/TimeSpanParser.cs
new file mode 100644
index 0000000..87994c3
--- /dev/null
+++ b/X10D/src/Time/TimeSpanParser.cs
@@ -0,0 +1,133 @@
+namespace X10D.Time;
+
+///
+/// Represents a class which contains a parser which converts into .
+///
+public static class TimeSpanParser
+{
+ ///
+ /// Attempts to parses a shorthand time span string (e.g. 3w 2d 1h), converting it to an instance of
+ /// which represents that duration of time.
+ ///
+ ///
+ /// The input string. Floating point is not supported, but range the following units are supported:
+ ///
+ ///
+ ///
+ /// Suffix
+ /// Meaning
+ ///
+ ///
+ /// -
+ /// ms
+ /// Milliseconds
+ ///
+ /// -
+ /// s
+ /// Seconds
+ ///
+ /// -
+ /// m
+ /// Minutes
+ ///
+ /// -
+ /// h
+ /// Hours
+ ///
+ /// -
+ /// d
+ /// Days
+ ///
+ /// -
+ /// w
+ /// Weeks
+ ///
+ /// -
+ /// mo
+ /// Months
+ ///
+ /// -
+ /// y
+ /// Years
+ ///
+ ///
+ ///
+ /// When this method returns, contains the parsed result.
+ /// if the parse was successful, otherwise.
+ /// is .
+ 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;
+ }
+}
diff --git a/X10D/src/TimeSpanParser.cs b/X10D/src/TimeSpanParser.cs
deleted file mode 100644
index 9f53f0d..0000000
--- a/X10D/src/TimeSpanParser.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using System.Globalization;
-using System.Text.RegularExpressions;
-
-namespace X10D;
-
-///
-/// Represents a class which contains a parser which converts into .
-///
-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);
-
- ///
- /// Attempts to parses a shorthand time span string (e.g. 3w 2d 1.5h), converting it to an instance of
- /// which represents that duration of time.
- ///
- /// The input string.
- /// The parsed result.
- /// The format provider.
- /// if the parse was successful, otherwise.
- 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;
- }
-}
\ No newline at end of file