Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 76 additions & 55 deletions Source/EnumValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
namespace Open.Text;

/// <summary>
/// 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.
/// </summary>
/// <remarks>String parsing or coercion is case sensitive and must be exact.</remarks>
[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<TEnum>
: IEquatable<EnumValue<TEnum>>, IEquatable<EnumValueCaseIgnored<TEnum>>, IEquatable<TEnum>
Expand All @@ -34,29 +35,25 @@ public readonly struct EnumValue<TEnum>
public static Type UnderlyingType => _underlyingType ??= Enum.GetUnderlyingType(typeof(TEnum));

/// <summary>
/// Constructs an EnumValue&lt;<typeparamref name="TEnum"/>&gt; using the provided enum value.
/// Constructs an <see cref="EnumValue{TEnum}"/> using the provided <typeparamref name="TEnum"/> value.
/// </summary>
public EnumValue(TEnum value)
{
Value = value;
}
=> Value = value;

/// <summary>
/// Parses the string value to construct an EnumValue&lt;<typeparamref name="TEnum"/>&gt; instance.
/// Parses the <paramref name="value"/> to construct an <see cref="EnumValue{TEnum}"/>.
/// </summary>
/// <exception cref="ArgumentNullException">value is null.</exception>
public EnumValue(StringSegment value)
{
Value = EnumValue.Parse<TEnum>(value);
}
=> Value = EnumValue.Parse<TEnum>(value);

/// <summary>
/// The enum value that this represents.
/// The <typeparamref name="TEnum"/> value that this represents.
/// </summary>
public readonly TEnum Value { get; }

/// <summary>
/// Returns the string representation of the enum value.
/// Returns the <see cref="string"/> representation of the enum value.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString() => NameLookup(Value);
Expand Down Expand Up @@ -165,7 +162,18 @@ internal static Func<string, ValueLookupResult> ValueLookup
private static IReadOnlyDictionary<string, TEnum>? _ignoreCaseLookup;
internal static IReadOnlyDictionary<string, TEnum> 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<string, TEnum>(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()
{
Expand Down Expand Up @@ -242,7 +250,6 @@ public static int Find(Span<Entry> span, ReadOnlySpan<char> name, StringComparis
return middle;
}


return -1;
}
}
Expand All @@ -253,49 +260,58 @@ internal static Entry[]?[] Lookup
() => CreateLookup())!;

/// <summary>
/// Indicates whether this instance matches the enum value of <paramref name="other"/>.
/// Indicates whether this instance matches the <typeparamref name="TEnum"/> value of <paramref name="other"/>.
/// </summary>
/// <returns>true if <paramref name="other"/>'s enum value and this instance's enum value are the same; otherwise false.</returns>
public bool Equals(EnumValue<TEnum> other) => Value.Equals(other.Value);
/// <returns>true if <paramref name="other"/>'s <typeparamref name="TEnum"/> value and this instance's <typeparamref name="TEnum"/> value are the same; otherwise false.</returns>
public bool Equals(EnumValue<TEnum> other)
=> Value.Equals(other.Value);

/// <summary>
/// Compares an EnumValue and EnumValueCaseIgnored for enum equality.
/// Compares an <see cref="EnumValue{TEnum}"/> and <see cref="EnumValueCaseIgnored{TEnum}"/> for <typeparamref name="TEnum"/> equality.
/// </summary>
public static bool operator ==(EnumValue<TEnum> left, EnumValue<TEnum> right) => left.Value.Equals(right.Value);
public static bool operator ==(EnumValue<TEnum> left, EnumValue<TEnum> right)
=> left.Value.Equals(right.Value);

/// <summary>
/// Compares an EnumValue and EnumValueCaseIgnored for enum inequality.
/// Compares an <see cref="EnumValue{TEnum}"/> and <see cref="EnumValueCaseIgnored{TEnum}"/> for <typeparamref name="TEnum"/> inequality.
/// </summary>
public static bool operator !=(EnumValue<TEnum> left, EnumValue<TEnum> right) => !left.Value.Equals(right.Value);
public static bool operator !=(EnumValue<TEnum> left, EnumValue<TEnum> right)
=> !left.Value.Equals(right.Value);

/// <inheritdoc cref="Equals(EnumValue{TEnum})"/>
public bool Equals(EnumValueCaseIgnored<TEnum> other) => Value.Equals(other.Value);
public bool Equals(EnumValueCaseIgnored<TEnum> other)
=> Value.Equals(other.Value);

/// <summary>
/// Compares two EnumValue for enum equality.
/// Compares two <see cref="EnumValue{TEnum}"/> for <typeparamref name="TEnum"/> equality.
/// </summary>
public static bool operator ==(EnumValue<TEnum> left, EnumValueCaseIgnored<TEnum> right) => left.Value.Equals(right.Value);
public static bool operator ==(EnumValue<TEnum> left, EnumValueCaseIgnored<TEnum> right)
=> left.Value.Equals(right.Value);

/// <summary>
/// Compares two EnumValue for enum inequality.
/// Compares two <see cref="EnumValue{TEnum}"/> for <typeparamref name="TEnum"/> inequality.
/// </summary>
public static bool operator !=(EnumValue<TEnum> left, EnumValueCaseIgnored<TEnum> right) => !left.Value.Equals(right.Value);
public static bool operator !=(EnumValue<TEnum> left, EnumValueCaseIgnored<TEnum> right)
=> !left.Value.Equals(right.Value);

/// <summary>
/// Indicates whether this instance matches the provided enum <paramref name="other"/>.
/// </summary>
/// <returns>true if <paramref name="other"/> and this instance's enum value are the same; otherwise false.</returns>
public bool Equals(TEnum other) => Value.Equals(other);
public bool Equals(TEnum other)
=> Value.Equals(other);

/// <summary>
/// Compares an EnumValue and an enum value for enum equality.
/// Compares an <see cref="EnumValue{TEnum}"/> and a <typeparamref name="TEnum"/> value forequality.
/// </summary>
public static bool operator ==(EnumValue<TEnum> left, TEnum right) => left.Value.Equals(right);
public static bool operator ==(EnumValue<TEnum> left, TEnum right)
=> left.Value.Equals(right);

/// <summary>
/// Compares an EnumValue and an enum value for enum inequality.
/// Compares an <see cref="EnumValue{TEnum}"/> and a <typeparamref name="TEnum"/> value for inequality.
/// </summary>
public static bool operator !=(EnumValue<TEnum> left, TEnum right) => !left.Value.Equals(right);
public static bool operator !=(EnumValue<TEnum> left, TEnum right)
=> !left.Value.Equals(right);

/// <inheritdoc />
public override bool Equals(object? obj)
Expand All @@ -307,23 +323,28 @@ public override bool Equals(object? obj)
public override int GetHashCode() => Value.GetHashCode();

/// <summary>
/// Implicitly converts an EnumValueCaseInsensitive to an EnumValue.
/// Implicitly converts an <see cref="EnumValueCaseIgnored{TEnum}"/> to an <see cref="EnumValue{TEnum}"/>.
/// Before conversion they are already equivalent.
/// </summary>
public static implicit operator EnumValue<TEnum>(EnumValueCaseIgnored<TEnum> value) => new(value.Value);

/// <summary>
/// Implicitly returns the actual enum contained by the EnumValue.
/// Implicitly returns the actual enum contained by the <see cref="EnumValue{TEnum}"/>.
/// </summary>
public static implicit operator TEnum(EnumValue<TEnum> value) => value.Value;

/// <summary>
/// Implicitly converts an string to an EnumValue of enum type TEnum.
/// Implicitly converts an <typeparamref name="TEnum"/> to an <see cref="EnumValue{TEnum}"/>.
/// </summary>
public static implicit operator EnumValue<TEnum>(TEnum value) => new(value);

/// <summary>
/// Implicitly converts a <see cref="string"/> to an <see cref="EnumValue{TEnum}"/>.
/// </summary>
public static implicit operator EnumValue<TEnum>(StringSegment value) => new(value);

/// <summary>
/// Implicitly converts an string to an EnumValue of enum type TEnum.
/// Implicitly converts a <see cref="string"/> to an <see cref="EnumValue{TEnum}"/>.
/// </summary>
public static implicit operator EnumValue<TEnum>(string value) => new(value);

Expand Down Expand Up @@ -374,21 +395,15 @@ public readonly struct EnumValueCaseIgnored<TEnum>
where TEnum : Enum
{
/// <summary>
/// Constructs an EnumValueCaseIgnored&lt;<typeparamref name="TEnum"/>&gt; using the provided enum value.
/// Constructs an <see cref="EnumValueCaseIgnored{TEnum}"/> using the provided enum value.
/// </summary>
public EnumValueCaseIgnored(TEnum value)
{
Value = value;
}
public EnumValueCaseIgnored(TEnum value) => Value = value;

/// <summary>
/// Parses the string value to construct an EnumValueCaseIgnored&lt;<typeparamref name="TEnum"/>&gt; instance.
/// Parses the string value to construct an <see cref="EnumValueCaseIgnored{TEnum}"/> instance.
/// </summary>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
public EnumValueCaseIgnored(StringSegment value)
{
Value = EnumValue.Parse<TEnum>(value, true);
}
public EnumValueCaseIgnored(StringSegment value) => Value = EnumValue.Parse<TEnum>(value, true);

/// <inheritdoc cref="EnumValue{TEnum}.Value"/>
public readonly TEnum Value { get; }
Expand All @@ -400,12 +415,12 @@ public EnumValueCaseIgnored(StringSegment value)
public bool Equals(EnumValue<TEnum> other) => Value.Equals(other.Value);

/// <summary>
/// Compares an EnumValueCaseIgnored and EnumValue for enum equality.
/// Compares an <see cref="EnumValueCaseIgnored{TEnum}"/> and <see cref="EnumValue{TEnum}"/> for <typeparamref name="TEnum"/> equality.
/// </summary>
public static bool operator ==(EnumValueCaseIgnored<TEnum> left, EnumValue<TEnum> right) => left.Value.Equals(right.Value);

/// <summary>
/// Compares an EnumValueCaseIgnored and EnumValue for enum inequality.
/// Compares an <see cref="EnumValueCaseIgnored{TEnum}"/> and <see cref="EnumValue{TEnum}"/> for <typeparamref name="TEnum"/> inequality.
/// </summary>
public static bool operator !=(EnumValueCaseIgnored<TEnum> left, EnumValue<TEnum> right)
=> !left.Value.Equals(right.Value);
Expand All @@ -415,13 +430,13 @@ public bool Equals(EnumValueCaseIgnored<TEnum> other)
=> Value.Equals(other.Value);

/// <summary>
/// Compares two EnumValueCaseIgnored for enum equality.
/// Compares two <see cref="EnumValueCaseIgnored{TEnum}"/> for <typeparamref name="TEnum"/> equality.
/// </summary>
public static bool operator ==(EnumValueCaseIgnored<TEnum> left, EnumValueCaseIgnored<TEnum> right)
=> left.Value.Equals(right.Value);

/// <summary>
/// Compares two EnumValueCaseIgnored for enum inequality.
/// Compares two <see cref="EnumValueCaseIgnored{TEnum}"/> for <typeparamref name="TEnum"/> inequality.
/// </summary>
public static bool operator !=(EnumValueCaseIgnored<TEnum> left, EnumValueCaseIgnored<TEnum> right)
=> !left.Value.Equals(right.Value);
Expand All @@ -431,13 +446,13 @@ public bool Equals(TEnum other)
=> Value.Equals(other);

/// <summary>
/// Compares an EnumValueCaseIgnored and an enum value for enum equality.
/// Compares an <see cref="EnumValueCaseIgnored{TEnum}"/> and a <typeparamref name="TEnum"/> value for equality.
/// </summary>
public static bool operator ==(EnumValueCaseIgnored<TEnum> left, TEnum right)
=> left.Value.Equals(right);

/// <summary>
/// Compares an EnumValueCaseIgnored and an enum value for enum inequality.
/// Compares an <see cref="EnumValueCaseIgnored{TEnum}"/> and a <typeparamref name="TEnum"/> value for inequality.
/// </summary>
public static bool operator !=(EnumValueCaseIgnored<TEnum> left, TEnum right)
=> !left.Value.Equals(right);
Expand All @@ -453,34 +468,40 @@ public override int GetHashCode()
=> Value.GetHashCode();

/// <summary>
/// Implicitly converts an EnumValue to an EnumValueCaseInsensitive.
/// Implicitly converts an <see cref="EnumValue{TEnum}"/> to an <see cref="EnumValueCaseIgnored{TEnum}"/>.
/// Before conversion they are already equivalent.
/// </summary>
public static implicit operator EnumValueCaseIgnored<TEnum>(EnumValue<TEnum> value)
=> new(value.Value);

/// <summary>
/// Implicitly returns the actual enum contained by the EnumValueCaseIgnored.
/// Implicitly returns the actual <typeparamref name="TEnum"/> contained by the <see cref="EnumValueCaseIgnored{TEnum}"/>.
/// </summary>
public static implicit operator TEnum(EnumValueCaseIgnored<TEnum> value)
=> value.Value;

/// <summary>
/// Implicitly converts an string to an EnumValueCaseIgnored of enum type TEnum.
/// Implicitly converts an <typeparamref name="TEnum"/> to an <see cref="EnumValue{TEnum}"/>.
/// </summary>
public static implicit operator EnumValueCaseIgnored<TEnum>(TEnum value)
=> new(value);

/// <summary>
/// Implicitly converts a <see cref="string"/> to an <see cref="EnumValueCaseIgnored{TEnum}"/>.
/// </summary>
public static implicit operator EnumValueCaseIgnored<TEnum>(StringSegment value)
=> new(value);

/// <summary>
/// Implicitly converts an string to an EnumValueCaseIgnored of enum type TEnum.
/// Implicitly converts a <see cref="string"/> to an <see cref="EnumValueCaseIgnored{TEnum}"/>.
/// </summary>
public static implicit operator EnumValueCaseIgnored<TEnum>(string value)
=> new(value);

private string GetDebuggerDisplay()
{
var eType = typeof(TEnum);
return $"{eType.Name}.{Value} [EnumValueCaseIgnored<{eType.FullName}>]";
return $"{eType.Name}.{ToString()} [EnumValueCaseIgnored<{eType.FullName}>]";
}
}

Expand Down
1 change: 0 additions & 1 deletion Source/Extensions._.cs
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,6 @@ public static int GetHashCodeFromChars(this ReadOnlySpan<char> chars, StringComp

break;
}

}

return hash + length;
Expand Down
2 changes: 1 addition & 1 deletion Source/Open.Text.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Text</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>string, span, enum, readonlyspan, text, format, split, trim, equals, trimmed equals, first, last, preceding, following, stringbuilder, extensions, stringcomparable, spancomparable, stringsegment, splitassegment</PackageTags>
<Version>6.6.3</Version>
<Version>6.7.0</Version>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
Expand Down
Loading