From d68f753da71ec73f608aa2745e8ecd53214feba7 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Fri, 2 Jun 2023 00:07:43 -0700 Subject: [PATCH 1/5] Checkpoint --- Source/StringSegmentSearch.cs | 254 ++++++++++++++++++++++++++++++++++ Source/StringSubsegment.cs | 114 +++++++++++++++ Tests/FindApiTests.cs | 43 ++++++ 3 files changed, 411 insertions(+) create mode 100644 Source/StringSegmentSearch.cs create mode 100644 Source/StringSubsegment.cs create mode 100644 Tests/FindApiTests.cs diff --git a/Source/StringSegmentSearch.cs b/Source/StringSegmentSearch.cs new file mode 100644 index 0000000..df4b4dc --- /dev/null +++ b/Source/StringSegmentSearch.cs @@ -0,0 +1,254 @@ +using Microsoft.Extensions.Primitives; +using System; +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; + +namespace Open.Text; +/// +/// Represents a search operation within a string segment. +/// +public readonly ref struct StringSegmentSearch +{ + /// + /// The source segment where the search is to be performed. + /// + public StringSegment Source { get; } + + /// + /// The character sequences to be searched within the source. + /// + public ReadOnlySpan Sequence { get; } + + /// + /// The string comparison option used for the search. + /// + public StringComparison Comparison { get; } + + /// + /// Indicates whether the search is to be performed from right to left. + /// + public bool RightToLeft { get; } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal StringSegmentSearch( + StringSegment source, + ReadOnlySpan search, + StringComparison comparisonType, + bool rightToLeft) + { + Source = source; + Sequence = search; + Comparison = comparisonType; + RightToLeft = rightToLeft; + } +} + +/// +/// Represents a captured substring within a string segment based on a search operation. +/// +public readonly ref struct StringSegmentCapture +{ + /// + /// Gets the search operation that resulted in this capture. + /// + public StringSegmentSearch Search { get; } + + /// + /// Gets the captured substring as a string subsegment. + /// + public StringSubsegment Value { get; } + + /// + /// Returns if the capture was successful; otherwise, . + /// + public bool Success => Value.HasValue; + + /// + /// The index of the first character in the source that is included in this subsegment. + /// + public int Index => Value.HasValue ? Value.Offset : -1; + + /// + /// Implicitly converts a to a . + /// + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates")] + public static implicit operator StringSegmentCapture(StringSegmentSearch capture) + => capture.First(); + + /// + /// Implicitly converts a to a . + /// + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates")] + public static implicit operator StringSubsegment(StringSegmentCapture capture) + => capture.Value; + + /// + /// Implicitly converts a to a . + /// + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates")] + public static implicit operator StringSegment(StringSegmentCapture capture) + => capture.Value; + + /// + public override string ToString() => Value.ToString(); + + /// + /// Initializes a new instance of the struct. + /// + /// + /// + /// + /// + /// + /// + internal StringSegmentCapture(StringSegmentSearch search, StringSubsegment value) + { + Search = search; + Value = value; + } +} + +public static partial class TextExtensions +{ + /// + /// Starts a search for the specified character sequence within the source segment. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static StringSegmentSearch Find( + this StringSegment source, + ReadOnlySpan search, + StringComparison comparisonType = StringComparison.Ordinal, + bool rightToLeft = false) + => new(source, search, comparisonType, rightToLeft); + + /// + public static StringSegmentSearch Find( + this string source, + ReadOnlySpan search, + StringComparison comparisonType = StringComparison.Ordinal, + bool rightToLeft = false) + => new(source, search, comparisonType, rightToLeft); + + /// + /// Finds the next occurrence of the specified character sequence within the source segment. + /// + public static StringSegmentCapture First( + this StringSegmentSearch search) + { + if(search.Source.Length == 0 || search.Sequence.Length == 0) + return default; + + var i = search.RightToLeft + ? search.Source.LastIndexOf(search.Sequence, search.Comparison) + : search.Source.IndexOf(search.Sequence, search.Comparison); + + return new(search, i == -1 + ? default + : new(search.Source, i, search.Sequence.Length) ); + } + + /// + /// Finds the next occurrence of the specified character sequence within the source segment. + /// + public static StringSegmentCapture Next( + this StringSegmentCapture capture) + { + var value = capture.Value; + var len = value.Length; + if (len == 0) + return default; + + var search = capture.Search; + var source = search.Source; + var i = capture.Search.RightToLeft + ? source.Subsegment(0, value.Offset).LastIndexOf(search.Sequence, search.Comparison) + : source.IndexOf(search.Sequence, value.Offset + value.Length, search.Comparison); + + return new(search, i == -1 + ? default + : new(source, i, len)); + } + + /// + /// Finds the next occurrence after the first occurrence of the specified character sequence within the source segment. + /// + public static StringSegmentCapture Next( + this StringSegmentSearch search) + => search.First().Next(); + + /// + /// Finds the last occurrence of the specified character sequence within the source segment. + /// + public static StringSegmentCapture Last( + this StringSegmentSearch search) + { + if (search.Source.Length == 0 || search.Sequence.Length == 0) + return default; + + var i = search.RightToLeft + ? search.Source.IndexOf(search.Sequence, search.Comparison) + : search.Source.LastIndexOf(search.Sequence, search.Comparison); + + return new(search, i == -1 + ? default + : new(search.Source, i, search.Sequence.Length)); + } + + /// + /// Returns if the capture has a value; otherwise . + /// + public static bool Exists(this StringSegmentCapture capture) + => capture.Value.HasValue; + + /// + /// Returns if the search has a value; otherwise . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Exists(this StringSegmentSearch search) + => search.First().Exists(); + + /// + /// Resolves the value of the capture, or returns the specified default value. + /// + public static StringSubsegment Or( + this StringSegmentCapture capture, + StringSubsegment defaultValue) + { + var value = capture.Value; + return value.HasValue ? value : defaultValue; + } + + /// + /// Resolves the value of the search, or returns the specified default value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static StringSubsegment Or( + this StringSegmentSearch capture, + StringSubsegment defaultValue) + => capture.First().Or(defaultValue); +} \ No newline at end of file diff --git a/Source/StringSubsegment.cs b/Source/StringSubsegment.cs new file mode 100644 index 0000000..91769c2 --- /dev/null +++ b/Source/StringSubsegment.cs @@ -0,0 +1,114 @@ +using Microsoft.Extensions.Primitives; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; + +namespace Open.Text; + +/// +/// A struct for representing a subsegment of a . +/// +public readonly struct StringSubsegment : IEquatable +{ + /// + /// Constructs a new from the specified . + /// + /// + /// + /// + internal StringSubsegment( + StringSegment source, + int offset, + int length) + { + Debug.Assert(source.HasValue); + Debug.Assert(offset >= 0 && offset <= source.Length); + Debug.Assert(length >= 0 && offset + length <= source.Length); + + Source = source; + Offset = offset; + Length = length; + } + + /// + /// Indicates that the is has a value. + /// + public bool HasValue => Source.HasValue; + + /// + /// The source from which this subsegment was created. + /// + public StringSegment Source { get; } + + /// + /// The index of the first character in the source that is included in this subsegment. + /// + public int Offset { get; } + + /// + /// The number of characters in the source that are included in this subsegment. + /// + public int Length { get; } + + /// + /// Gets a representing the defined subsegment. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan AsSpan() + => Source.AsSpan(Offset, Length); + + /// + /// Returns a new representing the defined subsegment. + /// + public StringSegment AsSegment() + => Offset == 0 && Source.Length == Length ? Source : Source.Subsegment(Offset, Length); + + /// + /// Returns a new representing the defined subsegment. + /// + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "AsSegment")] + public static implicit operator StringSegment(StringSubsegment segment) + => segment.AsSegment(); + + /// + /// Compares a read-only span of characters to this for equality. + /// + public bool Equals(ReadOnlySpan other, StringComparison comparisonType = StringComparison.Ordinal) + => Source.HasValue && other.Length == Length && AsSpan().Equals(other, comparisonType); + + /// + /// Compares two instances for equality. + /// + public bool Equals(StringSubsegment other, StringComparison comparisonType = StringComparison.Ordinal) + => Source.HasValue + ? other.Source.HasValue && other.Length == Length && Equals(other.AsSpan(), comparisonType) + : !other.Source.HasValue; + + /// + public override bool Equals(object obj) + => obj is StringSubsegment other && Equals(other); + + /// + public override int GetHashCode() + => AsSegment().GetHashCode(); + + /// + public override string ToString() + => AsSpan().ToString(); + + bool IEquatable.Equals(StringSubsegment other) + => Equals(other); + + /// + /// Compares two instances for equality. + /// + public static bool operator ==(StringSubsegment left, StringSubsegment right) + => left.Equals(right); + + /// + /// Compares two instances for inequality. + /// + public static bool operator !=(StringSubsegment left, StringSubsegment right) + => !left.Equals(right); +} \ No newline at end of file diff --git a/Tests/FindApiTests.cs b/Tests/FindApiTests.cs new file mode 100644 index 0000000..cb4b245 --- /dev/null +++ b/Tests/FindApiTests.cs @@ -0,0 +1,43 @@ +using FluentAssertions; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Open.Text.Tests; +public static class FindAPITests +{ + [Fact] + public static void Exists() + { + const string source = "Hello World!"; + StringSegment segment = source; + { + var e = segment.Find("Hello"); + e.Exists().Should().BeTrue(); + e.First().Success.Should().BeTrue(); + e.First().Index.Should().Be(0); + } + { + var e = segment.Find("xxx"); + e.Exists().Should().BeFalse(); + e.First().Success.Should().BeFalse(); + } + + { + var e = segment.Find("l").First(); + e.Success.Should().BeTrue(); + e.Index.Should().Be(2); + + e = e.Next(); + e.Success.Should().BeTrue(); + e.Index.Should().Be(3); + + e = e.Next(); + e.Success.Should().BeTrue(); + e.Index.Should().Be(9); + + e = e.Next(); + e.Success.Should().BeFalse(); + e.Index.Should().Be(-1); + } + } +} From 30487c1ff57042ab8e740cf7b72565d6524ac594 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Mon, 12 Jun 2023 11:16:50 -0700 Subject: [PATCH 2/5] Solve exception when building case insensitive lookup. --- Source/EnumValue.cs | 13 ++++++++++++- Source/Open.Text.csproj | 2 +- Tests/EnumValueTests.cs | 25 ++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/Source/EnumValue.cs b/Source/EnumValue.cs index 50851cf..48f4311 100644 --- a/Source/EnumValue.cs +++ b/Source/EnumValue.cs @@ -165,7 +165,18 @@ internal static Func ValueLookup private static IReadOnlyDictionary? _ignoreCaseLookup; internal static IReadOnlyDictionary IgnoreCaseLookup => LazyInitializer.EnsureInitialized(ref _ignoreCaseLookup, - () => Values.ToDictionary(e => Enum.GetName(typeof(TEnum), e)!, e => e, StringComparer.OrdinalIgnoreCase))!; + () => + { + // If the enum has duplicate values, we want to use the first one. + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var e in Values) + { + var v = Enum.GetName(typeof(TEnum), e); + if (result.ContainsKey(v)) continue; + result[v] = e; + } + return result; + })!; static Entry[]?[] CreateLookup() { diff --git a/Source/Open.Text.csproj b/Source/Open.Text.csproj index 1c6deb4..d969b56 100644 --- a/Source/Open.Text.csproj +++ b/Source/Open.Text.csproj @@ -19,7 +19,7 @@ https://github.com/Open-NET-Libraries/Open.Text git string, span, enum, readonlyspan, text, format, split, trim, equals, trimmed equals, first, last, preceding, following, stringbuilder, extensions, stringcomparable, spancomparable, stringsegment, splitassegment - 6.6.3 + 6.6.4 MIT true diff --git a/Tests/EnumValueTests.cs b/Tests/EnumValueTests.cs index f5c4fea..77098b8 100644 --- a/Tests/EnumValueTests.cs +++ b/Tests/EnumValueTests.cs @@ -1,4 +1,5 @@ -using System; +using FluentAssertions; +using System; using System.Linq; using Xunit; @@ -43,6 +44,15 @@ public enum Greek None } +public enum MultiCase +{ + Goodbye, + Hello, + HELLO, + hello, + goodbye +} + public enum LongEnum { In_the_glowing_moonlight_the_winds_caress_and_serenade_the_silent_ocean_waves, @@ -122,6 +132,19 @@ public enum LargeEnum public static class EnumValueTests { + [Fact] + public static void EnsureMultiCase() + { + EnumValue.TryParse("HELLO", out var value).Should().Be(true); + value.Should().Be(MultiCase.HELLO); + + EnumValue.TryParse("HELLO", true, out value).Should().Be(true); + value.Should().Be(MultiCase.Hello); + + EnumValue.TryParse("goodbye", true, out value).Should().Be(true); + value.Should().Be(MultiCase.Goodbye); + } + [Fact] public static void IsIntType() { From 60d6daf2ae699eaa301c73d01880c945455ab4cd Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:19:35 -0800 Subject: [PATCH 3/5] Cleanup. --- Source/EnumValue.cs | 2 +- Source/Extensions._.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/EnumValue.cs b/Source/EnumValue.cs index 48f4311..7404f04 100644 --- a/Source/EnumValue.cs +++ b/Source/EnumValue.cs @@ -20,6 +20,7 @@ namespace Open.Text; /// String parsing or coercion is case sensitive and must be exact. [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Already exposes via a property.")] [SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Intentional")] +[SuppressMessage("Roslynator", "RCS1158:Static member in generic type should use a type parameter.")] [DebuggerDisplay("{GetDebuggerDisplay()}")] public readonly struct EnumValue : IEquatable>, IEquatable>, IEquatable @@ -253,7 +254,6 @@ public static int Find(Span span, ReadOnlySpan name, StringComparis return middle; } - return -1; } } diff --git a/Source/Extensions._.cs b/Source/Extensions._.cs index ba7d9f2..8545a5c 100644 --- a/Source/Extensions._.cs +++ b/Source/Extensions._.cs @@ -484,7 +484,6 @@ public static int GetHashCodeFromChars(this ReadOnlySpan chars, StringComp break; } - } return hash + length; From 2e24a3a2c9bbd4315591ea0369368299fe86fa65 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:41:23 -0800 Subject: [PATCH 4/5] Tweaks, cleanup, better docs, and added addition implict conversion for EnumValue and EnumValueCaseIgnored. --- Source/EnumValue.cs | 116 ++++++++++++++++++++++------------------ Source/Open.Text.csproj | 2 +- 2 files changed, 64 insertions(+), 54 deletions(-) diff --git a/Source/EnumValue.cs b/Source/EnumValue.cs index 7404f04..01f01ba 100644 --- a/Source/EnumValue.cs +++ b/Source/EnumValue.cs @@ -15,7 +15,7 @@ namespace Open.Text; /// -/// A case struct representing an enum value that can be implicitly coerced from a string. +/// A struct representing an enum value that can be implicitly coerced from a string. /// /// String parsing or coercion is case sensitive and must be exact. [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Already exposes via a property.")] @@ -35,29 +35,25 @@ public readonly struct EnumValue public static Type UnderlyingType => _underlyingType ??= Enum.GetUnderlyingType(typeof(TEnum)); /// - /// Constructs an EnumValue<> using the provided enum value. + /// Constructs an using the provided value. /// public EnumValue(TEnum value) - { - Value = value; - } + => Value = value; /// - /// Parses the string value to construct an EnumValue<> instance. + /// Parses the to construct an . /// /// value is null. public EnumValue(StringSegment value) - { - Value = EnumValue.Parse(value); - } + => Value = EnumValue.Parse(value); /// - /// The enum value that this represents. + /// The value that this represents. /// public readonly TEnum Value { get; } /// - /// Returns the string representation of the enum value. + /// Returns the representation of the enum value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => NameLookup(Value); @@ -264,49 +260,58 @@ internal static Entry[]?[] Lookup () => CreateLookup())!; /// - /// Indicates whether this instance matches the enum value of . + /// Indicates whether this instance matches the value of . /// - /// true if 's enum value and this instance's enum value are the same; otherwise false. - public bool Equals(EnumValue other) => Value.Equals(other.Value); + /// true if 's value and this instance's value are the same; otherwise false. + public bool Equals(EnumValue other) + => Value.Equals(other.Value); /// - /// Compares an EnumValue and EnumValueCaseIgnored for enum equality. + /// Compares an and for equality. /// - public static bool operator ==(EnumValue left, EnumValue right) => left.Value.Equals(right.Value); + public static bool operator ==(EnumValue left, EnumValue right) + => left.Value.Equals(right.Value); /// - /// Compares an EnumValue and EnumValueCaseIgnored for enum inequality. + /// Compares an and for inequality. /// - public static bool operator !=(EnumValue left, EnumValue right) => !left.Value.Equals(right.Value); + public static bool operator !=(EnumValue left, EnumValue right) + => !left.Value.Equals(right.Value); /// - public bool Equals(EnumValueCaseIgnored other) => Value.Equals(other.Value); + public bool Equals(EnumValueCaseIgnored other) + => Value.Equals(other.Value); /// - /// Compares two EnumValue for enum equality. + /// Compares two for equality. /// - public static bool operator ==(EnumValue left, EnumValueCaseIgnored right) => left.Value.Equals(right.Value); + public static bool operator ==(EnumValue left, EnumValueCaseIgnored right) + => left.Value.Equals(right.Value); /// - /// Compares two EnumValue for enum inequality. + /// Compares two for inequality. /// - public static bool operator !=(EnumValue left, EnumValueCaseIgnored right) => !left.Value.Equals(right.Value); + public static bool operator !=(EnumValue left, EnumValueCaseIgnored right) + => !left.Value.Equals(right.Value); /// /// Indicates whether this instance matches the provided enum . /// /// true if and this instance's enum value are the same; otherwise false. - public bool Equals(TEnum other) => Value.Equals(other); + public bool Equals(TEnum other) + => Value.Equals(other); /// - /// Compares an EnumValue and an enum value for enum equality. + /// Compares an and a value forequality. /// - public static bool operator ==(EnumValue left, TEnum right) => left.Value.Equals(right); + public static bool operator ==(EnumValue left, TEnum right) + => left.Value.Equals(right); /// - /// Compares an EnumValue and an enum value for enum inequality. + /// Compares an and a value for inequality. /// - public static bool operator !=(EnumValue left, TEnum right) => !left.Value.Equals(right); + public static bool operator !=(EnumValue left, TEnum right) + => !left.Value.Equals(right); /// public override bool Equals(object? obj) @@ -318,23 +323,28 @@ public override bool Equals(object? obj) public override int GetHashCode() => Value.GetHashCode(); /// - /// Implicitly converts an EnumValueCaseInsensitive to an EnumValue. + /// Implicitly converts an to an . /// Before conversion they are already equivalent. /// public static implicit operator EnumValue(EnumValueCaseIgnored value) => new(value.Value); /// - /// Implicitly returns the actual enum contained by the EnumValue. + /// Implicitly returns the actual enum contained by the . /// public static implicit operator TEnum(EnumValue value) => value.Value; /// - /// Implicitly converts an string to an EnumValue of enum type TEnum. + /// Implicitly converts an to an . + /// + public static implicit operator EnumValue(TEnum value) => new(value); + + /// + /// Implicitly converts a to an . /// public static implicit operator EnumValue(StringSegment value) => new(value); /// - /// Implicitly converts an string to an EnumValue of enum type TEnum. + /// Implicitly converts a to an . /// public static implicit operator EnumValue(string value) => new(value); @@ -385,21 +395,15 @@ public readonly struct EnumValueCaseIgnored where TEnum : Enum { /// - /// Constructs an EnumValueCaseIgnored<> using the provided enum value. + /// Constructs an using the provided enum value. /// - public EnumValueCaseIgnored(TEnum value) - { - Value = value; - } + public EnumValueCaseIgnored(TEnum value) => Value = value; /// - /// Parses the string value to construct an EnumValueCaseIgnored<> instance. + /// Parses the string value to construct an instance. /// /// is null. - public EnumValueCaseIgnored(StringSegment value) - { - Value = EnumValue.Parse(value, true); - } + public EnumValueCaseIgnored(StringSegment value) => Value = EnumValue.Parse(value, true); /// public readonly TEnum Value { get; } @@ -411,12 +415,12 @@ public EnumValueCaseIgnored(StringSegment value) public bool Equals(EnumValue other) => Value.Equals(other.Value); /// - /// Compares an EnumValueCaseIgnored and EnumValue for enum equality. + /// Compares an and for equality. /// public static bool operator ==(EnumValueCaseIgnored left, EnumValue right) => left.Value.Equals(right.Value); /// - /// Compares an EnumValueCaseIgnored and EnumValue for enum inequality. + /// Compares an and for inequality. /// public static bool operator !=(EnumValueCaseIgnored left, EnumValue right) => !left.Value.Equals(right.Value); @@ -426,13 +430,13 @@ public bool Equals(EnumValueCaseIgnored other) => Value.Equals(other.Value); /// - /// Compares two EnumValueCaseIgnored for enum equality. + /// Compares two for equality. /// public static bool operator ==(EnumValueCaseIgnored left, EnumValueCaseIgnored right) => left.Value.Equals(right.Value); /// - /// Compares two EnumValueCaseIgnored for enum inequality. + /// Compares two for inequality. /// public static bool operator !=(EnumValueCaseIgnored left, EnumValueCaseIgnored right) => !left.Value.Equals(right.Value); @@ -442,13 +446,13 @@ public bool Equals(TEnum other) => Value.Equals(other); /// - /// Compares an EnumValueCaseIgnored and an enum value for enum equality. + /// Compares an and a value for equality. /// public static bool operator ==(EnumValueCaseIgnored left, TEnum right) => left.Value.Equals(right); /// - /// Compares an EnumValueCaseIgnored and an enum value for enum inequality. + /// Compares an and a value for inequality. /// public static bool operator !=(EnumValueCaseIgnored left, TEnum right) => !left.Value.Equals(right); @@ -464,26 +468,32 @@ public override int GetHashCode() => Value.GetHashCode(); /// - /// Implicitly converts an EnumValue to an EnumValueCaseInsensitive. + /// Implicitly converts an to an . /// Before conversion they are already equivalent. /// public static implicit operator EnumValueCaseIgnored(EnumValue value) => new(value.Value); /// - /// Implicitly returns the actual enum contained by the EnumValueCaseIgnored. + /// Implicitly returns the actual contained by the . /// public static implicit operator TEnum(EnumValueCaseIgnored value) => value.Value; /// - /// Implicitly converts an string to an EnumValueCaseIgnored of enum type TEnum. + /// Implicitly converts an to an . + /// + public static implicit operator EnumValueCaseIgnored(TEnum value) + => new(value); + + /// + /// Implicitly converts a to an . /// public static implicit operator EnumValueCaseIgnored(StringSegment value) => new(value); /// - /// Implicitly converts an string to an EnumValueCaseIgnored of enum type TEnum. + /// Implicitly converts a to an . /// public static implicit operator EnumValueCaseIgnored(string value) => new(value); @@ -491,7 +501,7 @@ public static implicit operator EnumValueCaseIgnored(string value) private string GetDebuggerDisplay() { var eType = typeof(TEnum); - return $"{eType.Name}.{Value} [EnumValueCaseIgnored<{eType.FullName}>]"; + return $"{eType.Name}.{ToString()} [EnumValueCaseIgnored<{eType.FullName}>]"; } } diff --git a/Source/Open.Text.csproj b/Source/Open.Text.csproj index d969b56..8ddaeb9 100644 --- a/Source/Open.Text.csproj +++ b/Source/Open.Text.csproj @@ -19,7 +19,7 @@ https://github.com/Open-NET-Libraries/Open.Text git string, span, enum, readonlyspan, text, format, split, trim, equals, trimmed equals, first, last, preceding, following, stringbuilder, extensions, stringcomparable, spancomparable, stringsegment, splitassegment - 6.6.4 + 6.7.0 MIT true From 101d083dea9c627b2d2ab7258f3bbc8689db1b78 Mon Sep 17 00:00:00 2001 From: electricessence <5899455+electricessence@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:48:49 -0800 Subject: [PATCH 5/5] Allow for bi-directional implicit conversion and apply specific Equals typing. --- Tests/EnumValueTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/EnumValueTests.cs b/Tests/EnumValueTests.cs index 77098b8..c2c78a8 100644 --- a/Tests/EnumValueTests.cs +++ b/Tests/EnumValueTests.cs @@ -216,7 +216,7 @@ public static void GetLetter(Greek expected, char letter) static void CheckImplicit(EnumValue value, Greek expected) { - Assert.Equal(expected, value); + Assert.Equal(expected, value); Assert.True(value == expected); Assert.True(value.Equals(expected)); Assert.True(value == new EnumValueCaseIgnored(expected)); @@ -226,7 +226,7 @@ static void CheckImplicit(EnumValue value, Greek expected) static void CheckImplicitCaseIgnored(EnumValueCaseIgnored value, Greek expected) { - Assert.Equal(expected, value); + Assert.Equal(expected, value); Assert.True(value == expected); Assert.True(value.Equals(expected)); Assert.True(value == new EnumValue(expected)); @@ -243,14 +243,14 @@ static void TryParseTestsCore() where T : Enum { var x = new EnumValue(e); - Assert.Equal(e, x); + Assert.Equal(e, x); Assert.Equal(e.GetHashCode(), x.GetHashCode()); Assert.Equal(e.ToString(), x.ToString()); } { var x = new EnumValueCaseIgnored(e); - Assert.Equal(e, x); + Assert.Equal(e, x); Assert.Equal(e.GetHashCode(), x.GetHashCode()); Assert.Equal(e.ToString(), x.ToString()); }