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());
}