From 987ff2f712c7f491993743a4f048caad51362287 Mon Sep 17 00:00:00 2001 From: smdn Date: Sat, 23 Apr 2022 20:32:30 +0900 Subject: [PATCH] implement IParseable and ISpanParseable --- .../Smdn.Fundamental.MimeType.csproj | 3 + .../Smdn/MimeType.IParseable.cs | 77 +++++++++-- .../Smdn/MimeTypeStringExtensions.cs | 2 + .../Smdn.Fundamental.MimeType.Tests.csproj | 3 + .../Smdn/MimeType.cs | 130 ++++++++++++++++-- 5 files changed, 197 insertions(+), 18 deletions(-) diff --git a/src/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.csproj b/src/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.csproj index befa046fe..414e23f9a 100644 --- a/src/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.csproj +++ b/src/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.csproj @@ -18,6 +18,9 @@ SPDX-License-Identifier: MIT MIME;MIME-type + + + diff --git a/src/Smdn.Fundamental.MimeType/Smdn/MimeType.IParseable.cs b/src/Smdn.Fundamental.MimeType/Smdn/MimeType.IParseable.cs index b3fae780e..6099dfbaa 100644 --- a/src/Smdn.Fundamental.MimeType/Smdn/MimeType.IParseable.cs +++ b/src/Smdn.Fundamental.MimeType/Smdn/MimeType.IParseable.cs @@ -20,9 +20,6 @@ partial class MimeType #pragma warning restore IDE0040 #if FEATURE_GENERIC_MATH : -#if !OBSOLETE_MEMBER - IParseable, -#endif ISpanParseable #endif { @@ -43,14 +40,57 @@ public static bool TryParse( [NotNullWhen(true)] #endif out MimeType? result + ) + => TryParse(s, provider: null, out result); + + // IParseable.TryParse + public static bool TryParse( + string? s, + IFormatProvider? provider, +#if NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES + [NotNullWhen(true)] +#endif + out MimeType result ) { - result = null; + result = null!; if (s is null) return false; - if (!TryParse(s.AsSpan(), nameof(s), onParseError: OnParseError.ReturnFalse, out var ret)) + + if ( + !TryParse( + s.AsSpan(), + nameof(s), + onParseError: OnParseError.ReturnFalse, + provider: provider, + out var ret + ) + ) { return false; + } + + result = new(ret); + + return true; + } + + // ISpanParseable.TryParse + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out MimeType result) + { + result = null!; + + if ( + !TryParse( + s, + nameof(s), + onParseError: OnParseError.ReturnFalse, + provider: provider, + out var ret + ) + ) { + return false; + } result = new(ret); @@ -63,19 +103,35 @@ out MimeType? result public static (string type, string subType) Parse(string s) => MimeTypeStringExtensions.Split(s); #pragma warning restore SA1316 -#else - public static MimeType Parse(string s) +#endif + + // IParseable.Parse + public static MimeType Parse(string s, IFormatProvider? provider = null) { TryParse( s: (s ?? throw new ArgumentNullException(nameof(s))).AsSpan(), paramName: nameof(s), - throwIfInvalid: OnParseError.ThrowFormatException, + onParseError: OnParseError.ThrowFormatException, + provider: provider, + out var result + ); + + return new(result.Type, result.SubType); + } + + // ISpanParseable.Parse + public static MimeType Parse(ReadOnlySpan s, IFormatProvider? provider = null) + { + TryParse( + s: s, + paramName: nameof(s), + onParseError: OnParseError.ThrowFormatException, + provider: provider, out var result ); return new(result.Type, result.SubType); } -#endif internal enum OnParseError { ThrowFormatException, @@ -87,6 +143,9 @@ internal static bool TryParse( ReadOnlySpan s, string paramName, OnParseError onParseError, +#pragma warning disable IDE0060 + IFormatProvider? provider, +#pragma warning restore IDE0060 out (string Type, string SubType) result ) { diff --git a/src/Smdn.Fundamental.MimeType/Smdn/MimeTypeStringExtensions.cs b/src/Smdn.Fundamental.MimeType/Smdn/MimeTypeStringExtensions.cs index 21ad90fbe..24a879248 100644 --- a/src/Smdn.Fundamental.MimeType/Smdn/MimeTypeStringExtensions.cs +++ b/src/Smdn.Fundamental.MimeType/Smdn/MimeTypeStringExtensions.cs @@ -21,6 +21,7 @@ public static bool TrySplit( s: mimeType.AsSpan(), paramName: nameof(mimeType), onParseError: MimeType.OnParseError.ReturnFalse, + provider: null, out result ); } @@ -40,6 +41,7 @@ string paramName s: mimeType.AsSpan(), paramName: paramName, onParseError: MimeType.OnParseError.ThrowArgumentException, + provider: null, out var result ); diff --git a/tests/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.Tests.csproj b/tests/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.Tests.csproj index d52d47792..8ccf262ea 100644 --- a/tests/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.Tests.csproj +++ b/tests/Smdn.Fundamental.MimeType/Smdn.Fundamental.MimeType.Tests.csproj @@ -11,4 +11,7 @@ SPDX-License-Identifier: MIT + + + diff --git a/tests/Smdn.Fundamental.MimeType/Smdn/MimeType.cs b/tests/Smdn.Fundamental.MimeType/Smdn/MimeType.cs index c4bd08c6b..9e0697409 100644 --- a/tests/Smdn.Fundamental.MimeType/Smdn/MimeType.cs +++ b/tests/Smdn.Fundamental.MimeType/Smdn/MimeType.cs @@ -239,26 +239,138 @@ private static System.Collections.IEnumerable YieldParseInvalidFormatTestCases() yield return new object[] { "text/plain/foo", typeof(FormatException) }; } -#if false [TestCaseSource(nameof(YieldParseValidTestCases))] public void TestParse(string s, MimeType expected) - => Assert.AreEqual((expected.Type, expected.SubType), MimeType.Parse(s)); -#endif + => Assert.AreEqual(expected, MimeType.Parse(s, provider: null)); + + [TestCaseSource(nameof(YieldParseValidTestCases))] + public void TestParse_ReadOnlySpanOfChar(string s, MimeType expected) + => Assert.AreEqual(expected, MimeType.Parse(s.AsSpan(), provider: null)); [TestCaseSource(nameof(YieldParseValidTestCases))] public void TestTryParse(string s, MimeType expected) { - Assert.IsTrue(MimeType.TryParse(s, out MimeType result)); + Assert.IsTrue(MimeType.TryParse(s, provider: null, out var result)); Assert.AreEqual(expected, result); } -#if false + [TestCaseSource(nameof(YieldParseValidTestCases))] + public void TestTryParse_ReadOnlySpanOfChar(string s, MimeType expected) + { + Assert.IsTrue(MimeType.TryParse(s.AsSpan(), provider: null, out var result)); + Assert.AreEqual(expected, result); + } + +#if FEATURE_GENERIC_MATH + [TestCaseSource(nameof(YieldParseValidTestCases))] + public void IParseable_Parse(string s, MimeType expected) + { + Assert.AreEqual(expected, Parse(s)); + + static TSelf Parse(string s) where TSelf : IParseable + => TSelf.Parse(s, provider: null); + } + + [TestCaseSource(nameof(YieldParseValidTestCases))] + public void ISpanParseable_Parse(string s, MimeType expected) + { + Assert.AreEqual(expected, Parse(s.AsSpan())); + + static TSelf Parse(ReadOnlySpan s) where TSelf : ISpanParseable + => TSelf.Parse(s, provider: null); + } + + [TestCaseSource(nameof(YieldParseValidTestCases))] + public void IParseable_TryParse(string s, MimeType expected) + { + Assert.IsTrue(TryParse(s, out var result)); + Assert.AreEqual(expected, result); + + static bool TryParse(string s, out TSelf result) where TSelf : IParseable + => TSelf.TryParse(s, provider: null, out result); + } + + [TestCaseSource(nameof(YieldParseValidTestCases))] + public void ISpanParseable_TryParse(string s, MimeType expected) + { + Assert.IsTrue(TryParse(s.AsSpan(), out var result)); + Assert.AreEqual(expected, result); + + static bool TryParse(ReadOnlySpan s, out TSelf result) where TSelf : ISpanParseable + => TSelf.TryParse(s, provider: null, out result); + } +#endif + [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] public void TestParse_InvalidFormat(string s, Type expectedExceptionType) - => Assert.Throws(expectedExceptionType, () => MimeType.Parse(s)); -#endif + => Assert.Throws(expectedExceptionType, () => MimeType.Parse(s, provider: null)); + + [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] + public void TestParse_ReadOnlySpanOfChar_InvalidFormat(string s, Type expectedExceptionType) + { + if (s is null) + Assert.Pass(); + + Assert.Throws(expectedExceptionType, () => MimeType.Parse(s.AsSpan(), provider: null)); + } [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] - public void TestTryParse_InvalidFormat(string s, Type _) - => Assert.IsFalse(MimeType.TryParse(s, out MimeType _)); + public void TestTryParse_InvalidFormat(string s, Type discard) + => Assert.IsFalse(MimeType.TryParse(s, provider: null, out _)); + + [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] + public void TestTryParse_ReadOnlySpanOfChar_InvalidFormat(string s, Type discard) + { + if (s is null) + Assert.Pass(); + + Assert.IsFalse(MimeType.TryParse(s.AsSpan(), provider: null, out _)); + } + +#if FEATURE_GENERIC_MATH + [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] + public void IParseable_Parse_InvalidFormat(string s, Type expectedExceptionType) + { + Assert.Throws(expectedExceptionType, () => Parse(s)); + + static TSelf Parse(string s) where TSelf : IParseable + => TSelf.Parse(s, provider: null); + } + + [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] + public void ISpanParseable_Parse_InvalidFormat(string s, Type expectedExceptionType) + { + if (s is null) + Assert.Pass(); + + Assert.Throws(expectedExceptionType, () => Parse(s.AsSpan())); + + static TSelf Parse(ReadOnlySpan s) where TSelf : ISpanParseable + => TSelf.Parse(s, provider: null); + } + + [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] + public void IParseable_TryParse_InvalidFormat(string s, Type discard) + { + if (s is null) + Assert.Pass(); + + Assert.IsFalse(TryParse(s, out _)); + + static bool TryParse(string s, out TSelf result) where TSelf : IParseable + => TSelf.TryParse(s, provider: null, out result); + } + + [TestCaseSource(nameof(YieldParseInvalidFormatTestCases))] + public void ISpanParseable_TryParse_InvalidFormat(string s, Type discard) + { + if (s is null) + Assert.Pass(); + + Assert.IsFalse(TryParse(s.AsSpan(), out _)); + + static bool TryParse(ReadOnlySpan s, out TSelf result) where TSelf : ISpanParseable + => TSelf.TryParse(s, provider: null, out result); + } +#endif }