diff --git a/src/NetEscapades.EnumGenerators.Generators/SourceGenerationHelper.cs b/src/NetEscapades.EnumGenerators.Generators/SourceGenerationHelper.cs index 866e0d4..918c706 100644 --- a/src/NetEscapades.EnumGenerators.Generators/SourceGenerationHelper.cs +++ b/src/NetEscapades.EnumGenerators.Generators/SourceGenerationHelper.cs @@ -94,7 +94,35 @@ namespace /// This is a non-distinct count of defined names. /// public const int Length = - """).Append(enumToGenerate.Names.Count).Append(";").Append( + """).Append(enumToGenerate.Names.Count).Append(";"); + + if (enumToGenerate.HasFlags) + { + sb.Append( + """ + + + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const + """).Append(' ').Append(fullyQualifiedName).Append(" All = "); + + var first = true; + foreach (var member in enumToGenerate.Names) + { + if (!first) + { + sb.Append(" | "); + } + first = false; + sb.Append(fullyQualifiedName).Append('.').AppendIdentifier(member.Key); + } + + sb.Append(';'); + } + + sb.Append( """ @@ -550,7 +578,123 @@ public static bool HasFlagFast(this """ flag) => flag == 0 ? true : (value & flag) == flag; + + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this + """).Append(' ').Append(fullyQualifiedName).Append(" value, ").Append(fullyQualifiedName) + .Append( + """ + otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; """); + + // Collect single-bit flag members (powers of two) and build a mask of all defined bits. + // Composite members are allowed as long as every bit they set is also a defined single-bit flag. + // Members with bits outside the defined mask disqualify the enum from TryGetFlags emission. + List<(string Name, ulong Value)>? singleBitFlags = null; + var seenBitValues = new HashSet(); + ulong definedMask = 0; + foreach (var member in enumToGenerate.Names) + { + var bits = ToUInt64BitPattern(member.Value.ConstantValue); + if (bits == 0) + { + // Zero-valued members (e.g. None) are allowed but not emitted. + continue; + } + if ((bits & (bits - 1)) == 0 && seenBitValues.Add(bits)) + { + singleBitFlags ??= new List<(string, ulong)>(); + singleBitFlags.Add((member.Key, bits)); + definedMask |= bits; + } + } + + // Verify every non-zero member value is a subset of the defined-bit mask. + // Catches members with bits that don't correspond to any single-bit flag. + var hasInvalidValue = false; + if (singleBitFlags is { Count: > 0 }) + { + foreach (var member in enumToGenerate.Names) + { + var bits = ToUInt64BitPattern(member.Value.ConstantValue); + if (bits != 0 && (bits & ~definedMask) != 0) + { + hasInvalidValue = true; + break; + } + } + } + + if (!hasInvalidValue && singleBitFlags is { Count: > 0 }) + { + singleBitFlags.Sort((a, b) => a.Value.CompareTo(b.Value)); + + sb.Append( + """ + + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = + """).Append(' ').Append(singleBitFlags.Count).Append(';'); + + sb.Append( + """ + + + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this + """).Append(' ').Append(fullyQualifiedName).Append(" value, global::System.Span<").Append(fullyQualifiedName).Append( + """ + > buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + """); + + foreach (var flag in singleBitFlags) + { + sb.Append("\n if ((value & ") + .Append(fullyQualifiedName).Append('.').AppendIdentifier(flag.Name) + .Append(") == ") + .Append(fullyQualifiedName).Append('.').AppendIdentifier(flag.Name) + .Append(")\n buffer[count++] = ") + .Append(fullyQualifiedName).Append('.').AppendIdentifier(flag.Name) + .Append(';'); + } + + sb.Append( + """ + + return true; + } + #endif + """); + } } sb.Append( @@ -2380,6 +2524,20 @@ private static StringBuilder AppendIdentifier(this StringBuilder sb, string iden return sb.Append(identifier); } + private static ulong ToUInt64BitPattern(object value) + => value switch + { + sbyte v => (byte)v, + byte v => v, + short v => (ushort)v, + ushort v => v, + int v => (uint)v, + uint v => v, + long v => unchecked((ulong)v), + ulong v => v, + _ => 0UL, + }; + private enum AlternativeMethodChoice { None, diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/BitFlagsEnumExtensionsTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/BitFlagsEnumExtensionsTests.cs new file mode 100644 index 0000000..d5acec0 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/BitFlagsEnumExtensionsTests.cs @@ -0,0 +1,76 @@ +using FluentAssertions; +using System; +using Xunit; + +#if INTEGRATION_TESTS +namespace NetEscapades.EnumGenerators.IntegrationTests; +#elif PRIVATEASSETS_INTEGRATION_TESTS +namespace NetEscapades.EnumGenerators.PrivateAssets.IntegrationTests; +#elif NETSTANDARD_INTEGRATION_TESTS +namespace NetEscapades.EnumGenerators.NetStandard.IntegrationTests; +#elif NETSTANDARD_SYSTEMMEMORY_INTEGRATION_TESTS +namespace NetEscapades.EnumGenerators.NetStandard.SystemMemory.IntegrationTests; +#elif INTERCEPTOR_TESTS +namespace NetEscapades.EnumGenerators.Interceptors.IntegrationTests; +#elif NUGET_INTEGRATION_TESTS +namespace NetEscapades.EnumGenerators.Nuget.IntegrationTests; +#elif NUGET_INTERCEPTOR_TESTS +namespace NetEscapades.EnumGenerators.Nuget.Interceptors.IntegrationTests; +#elif NUGET_SYSTEMMEMORY_INTEGRATION_TESTS +namespace NetEscapades.EnumGenerators.Nuget.SystemMemory.IntegrationTests; +#elif NUGET_SYSTEMMEMORY_PRIVATEASSETS_INTEGRATION_TESTS +namespace NetEscapades.EnumGenerators.Nuget.SystemMemory.PrivateAssets.IntegrationTests; +#else +#error Unknown integration tests +#endif + +public class BitFlagsEnumExtensionsTests +{ + [Fact] + public void DistinctFlagCountExcludesNone() + { + BitFlagsEnumExtensions.DistinctFlagCount.Should().Be(4); + } + +#if READONLYSPAN + public static TheoryData TryGetFlagsData => new() + { + // Single flag → itself + { BitFlagsEnum.First, 1, new[] { BitFlagsEnum.First } }, + // Multiple flags — bit-value order + { BitFlagsEnum.First | BitFlagsEnum.Third, 2, new[] { BitFlagsEnum.First, BitFlagsEnum.Third } }, + // None → empty output + { BitFlagsEnum.None, 0, Array.Empty() }, + // Undefined bits silently dropped (16 is not defined) + { (BitFlagsEnum)17, 1, new[] { BitFlagsEnum.First } }, + // Only undefined bits → empty output + { (BitFlagsEnum)16, 0, Array.Empty() }, + // All defined flags set → 4 single-bit members + { BitFlagsEnumExtensions.All, 4, + new[] { BitFlagsEnum.First, BitFlagsEnum.Second, BitFlagsEnum.Third, BitFlagsEnum.Fourth } }, + // All bits set across underlying type → still just the 4 defined flags + { (BitFlagsEnum)0xFF, 4, + new[] { BitFlagsEnum.First, BitFlagsEnum.Second, BitFlagsEnum.Third, BitFlagsEnum.Fourth } }, + }; + + [Theory] + [MemberData(nameof(TryGetFlagsData))] + public void TryGetFlags(BitFlagsEnum value, int expectedCount, BitFlagsEnum[] expectedFlags) + { + Span buffer = stackalloc BitFlagsEnum[BitFlagsEnumExtensions.DistinctFlagCount]; + var result = value.TryGetFlags(buffer, out var count); + result.Should().BeTrue(); + count.Should().Be(expectedCount); + buffer.Slice(0, count).ToArray().Should().Equal(expectedFlags); + } + + [Fact] + public void TryGetFlags_BufferSmallerThanDistinctFlagCount_ReturnsFalse() + { + Span buffer = stackalloc BitFlagsEnum[BitFlagsEnumExtensions.DistinctFlagCount - 1]; + var result = BitFlagsEnum.First.TryGetFlags(buffer, out var count); + result.Should().BeFalse(); + count.Should().Be(0); + } +#endif +} diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs index 2be7a12..0059824 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs @@ -167,6 +167,17 @@ public enum FlagsEnum ThirdAndFourth = Third | Fourth, } + [EnumExtensions] + [Flags] + public enum BitFlagsEnum + { + None = 0, + First = 1 << 0, + Second = 1 << 1, + Third = 1 << 2, + Fourth = 1 << 3, + } + [EnumExtensions(MetadataSource = MetadataSource.DescriptionAttribute)] public enum StringTesting { diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs index c980c41..21cfbb8 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs @@ -139,7 +139,104 @@ public void HasFlags(FlagsEnum value, FlagsEnum flag) isDefined.Should().Be(value.HasFlag(flag)); } - + + [Fact] + public void AllContainsFlag() + { + foreach (var flag in FlagsEnumExtensions.GetValues()) + { + FlagsEnumExtensions.All.HasFlag(flag).Should().BeTrue(); + } + } + + [Fact] + public void AllHasExpectedValue() + { + ((int)FlagsEnumExtensions.All).Should().Be(0b1111); // 15 + } + + public static TheoryData HasAnyFlagsData => new() + { + // Single flag present + { FlagsEnum.First | FlagsEnum.Second, FlagsEnum.First, true }, + // No queried flags present + { FlagsEnum.First | FlagsEnum.Second, FlagsEnum.Third, false }, + // Partial overlap — only some flags match + { FlagsEnum.First, FlagsEnum.First | FlagsEnum.Third, true }, + // None has no flags + { FlagsEnum.None, FlagsEnum.First, false }, + // Querying with zero (None) — consistent with HasFlag(0) behavior + { FlagsEnum.First, FlagsEnum.None, true }, + // Both zero + { FlagsEnum.None, FlagsEnum.None, true }, + // Composite member overlaps + { FlagsEnum.Third, FlagsEnum.ThirdAndFourth, true }, + // Composite member no overlap + { FlagsEnum.First, FlagsEnum.ThirdAndFourth, false }, + // Undefined value with overlap (65 = 64|1, First = 1) + { (FlagsEnum)65, FlagsEnum.First, true }, + // Undefined value no overlap + { (FlagsEnum)64, FlagsEnum.First, false }, + }; + + [Theory] + [MemberData(nameof(HasAnyFlagsData))] + public void HasAnyFlags(FlagsEnum value, FlagsEnum otherFlags, bool expected) + { + value.HasAnyFlags(otherFlags).Should().Be(expected); + } + + [Fact] + public void DistinctFlagCountCountsSingleBitFlags() + { + // First, Second, Third, Fourth — ThirdAndFourth is a composite, None is zero + FlagsEnumExtensions.DistinctFlagCount.Should().Be(4); + } + +#if READONLYSPAN + public static TheoryData TryGetFlagsData => new() + { + // Single flag + { FlagsEnum.First, 1, new[] { FlagsEnum.First } }, + // Multiple flags — bit-value order + { FlagsEnum.First | FlagsEnum.Third, 2, new[] { FlagsEnum.First, FlagsEnum.Third } }, + // Composite member decomposes into its constituent single-bit flags + { FlagsEnum.ThirdAndFourth, 2, new[] { FlagsEnum.Third, FlagsEnum.Fourth } }, + // Combination of single-bit and composite — same as combining the bits + { FlagsEnum.First | FlagsEnum.ThirdAndFourth, 3, + new[] { FlagsEnum.First, FlagsEnum.Third, FlagsEnum.Fourth } }, + // None → empty + { FlagsEnum.None, 0, Array.Empty() }, + // Undefined bit (16) is silently dropped, defined bit (1) is kept + { (FlagsEnum)17, 1, new[] { FlagsEnum.First } }, + // Only undefined bits → empty + { (FlagsEnum)16, 0, Array.Empty() }, + // All defined flags set + { FlagsEnumExtensions.All, 4, + new[] { FlagsEnum.First, FlagsEnum.Second, FlagsEnum.Third, FlagsEnum.Fourth } }, + }; + + [Theory] + [MemberData(nameof(TryGetFlagsData))] + public void TryGetFlags(FlagsEnum value, int expectedCount, FlagsEnum[] expectedFlags) + { + Span buffer = stackalloc FlagsEnum[FlagsEnumExtensions.DistinctFlagCount]; + var result = value.TryGetFlags(buffer, out var count); + result.Should().BeTrue(); + count.Should().Be(expectedCount); + buffer.Slice(0, count).ToArray().Should().Equal(expectedFlags); + } + + [Fact] + public void TryGetFlags_BufferSmallerThanDistinctFlagCount_ReturnsFalse() + { + Span buffer = stackalloc FlagsEnum[FlagsEnumExtensions.DistinctFlagCount - 1]; + var result = FlagsEnum.First.TryGetFlags(buffer, out var count); + result.Should().BeFalse(); + count.Should().Be(0); + } +#endif + private PackageEnumParseOptions Map(EnumParseOptions options) => new(comparisonType: options.ComparisonType, allowMatchingMetadataAttribute: options.AllowMatchingMetadataAttribute, diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsForFlagsEnum_Params.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsForFlagsEnum_Params.verified.txt index a5a863e..120bc4b 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsForFlagsEnum_Params.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsForFlagsEnum_Params.verified.txt @@ -25,6 +25,11 @@ namespace MyTestNameSpace /// public const int Length = 3; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::MyTestNameSpace.MyEnum All = global::MyTestNameSpace.MyEnum.First | global::MyTestNameSpace.MyEnum.Second | global::MyTestNameSpace.MyEnum.Third; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -103,6 +108,52 @@ namespace MyTestNameSpace public static bool HasFlagFast(this global::MyTestNameSpace.MyEnum value, global::MyTestNameSpace.MyEnum flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::MyTestNameSpace.MyEnum value, global::MyTestNameSpace.MyEnum otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 3; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::MyTestNameSpace.MyEnum value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::MyTestNameSpace.MyEnum.First) == global::MyTestNameSpace.MyEnum.First) + buffer[count++] = global::MyTestNameSpace.MyEnum.First; + if ((value & global::MyTestNameSpace.MyEnum.Second) == global::MyTestNameSpace.MyEnum.Second) + buffer[count++] = global::MyTestNameSpace.MyEnum.Second; + if ((value & global::MyTestNameSpace.MyEnum.Third) == global::MyTestNameSpace.MyEnum.Third) + buffer[count++] = global::MyTestNameSpace.MyEnum.Third; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateForExternalFlagsEnum.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateForExternalFlagsEnum.verified.txt index 7dc6b87..3e06968 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateForExternalFlagsEnum.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateForExternalFlagsEnum.verified.txt @@ -25,6 +25,11 @@ namespace System.IO /// public const int Length = 6; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::System.IO.FileShare All = global::System.IO.FileShare.None | global::System.IO.FileShare.Read | global::System.IO.FileShare.Write | global::System.IO.FileShare.ReadWrite | global::System.IO.FileShare.Delete | global::System.IO.FileShare.Inheritable; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -112,6 +117,54 @@ namespace System.IO public static bool HasFlagFast(this global::System.IO.FileShare value, global::System.IO.FileShare flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::System.IO.FileShare value, global::System.IO.FileShare otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 4; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::System.IO.FileShare value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::System.IO.FileShare.Read) == global::System.IO.FileShare.Read) + buffer[count++] = global::System.IO.FileShare.Read; + if ((value & global::System.IO.FileShare.Write) == global::System.IO.FileShare.Write) + buffer[count++] = global::System.IO.FileShare.Write; + if ((value & global::System.IO.FileShare.Delete) == global::System.IO.FileShare.Delete) + buffer[count++] = global::System.IO.FileShare.Delete; + if ((value & global::System.IO.FileShare.Inheritable) == global::System.IO.FileShare.Inheritable) + buffer[count++] = global::System.IO.FileShare.Inheritable; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanHandleEnumsWithSameNameInDifferentNamespaces.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanHandleEnumsWithSameNameInDifferentNamespaces.verified.txt index 3043fbb..991f911 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanHandleEnumsWithSameNameInDifferentNamespaces.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanHandleEnumsWithSameNameInDifferentNamespaces.verified.txt @@ -26,6 +26,11 @@ namespace Foo /// public const int Length = 3; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Foo.MyEnum All = global::Foo.MyEnum.First | global::Foo.MyEnum.Second | global::Foo.MyEnum.Third; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -104,6 +109,52 @@ namespace Foo public static bool HasFlagFast(this global::Foo.MyEnum value, global::Foo.MyEnum flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Foo.MyEnum value, global::Foo.MyEnum otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 3; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::Foo.MyEnum value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::Foo.MyEnum.First) == global::Foo.MyEnum.First) + buffer[count++] = global::Foo.MyEnum.First; + if ((value & global::Foo.MyEnum.Second) == global::Foo.MyEnum.Second) + buffer[count++] = global::Foo.MyEnum.Second; + if ((value & global::Foo.MyEnum.Third) == global::Foo.MyEnum.Third) + buffer[count++] = global::Foo.MyEnum.Third; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. @@ -866,6 +917,11 @@ namespace Bar /// public const int Length = 3; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Bar.MyEnum All = global::Bar.MyEnum.First | global::Bar.MyEnum.Second | global::Bar.MyEnum.Third; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -944,6 +1000,52 @@ namespace Bar public static bool HasFlagFast(this global::Bar.MyEnum value, global::Bar.MyEnum flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Bar.MyEnum value, global::Bar.MyEnum otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 3; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::Bar.MyEnum value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::Bar.MyEnum.First) == global::Bar.MyEnum.First) + buffer[count++] = global::Bar.MyEnum.First; + if ((value & global::Bar.MyEnum.Second) == global::Bar.MyEnum.Second) + buffer[count++] = global::Bar.MyEnum.Second; + if ((value & global::Bar.MyEnum.Third) == global::Bar.MyEnum.Third) + buffer[count++] = global::Bar.MyEnum.Third; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInDifferentNamespace.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInDifferentNamespace.verified.txt index a4155f9..f255908 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInDifferentNamespace.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInDifferentNamespace.verified.txt @@ -26,6 +26,11 @@ namespace Bar /// public const int Length = 3; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Foo.MyEnum All = global::Foo.MyEnum.First | global::Foo.MyEnum.Second | global::Foo.MyEnum.Third; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -104,6 +109,52 @@ namespace Bar public static bool HasFlagFast(this global::Foo.MyEnum value, global::Foo.MyEnum flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Foo.MyEnum value, global::Foo.MyEnum otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 3; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::Foo.MyEnum value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::Foo.MyEnum.First) == global::Foo.MyEnum.First) + buffer[count++] = global::Foo.MyEnum.First; + if ((value & global::Foo.MyEnum.Second) == global::Foo.MyEnum.Second) + buffer[count++] = global::Foo.MyEnum.Second; + if ((value & global::Foo.MyEnum.Third) == global::Foo.MyEnum.Third) + buffer[count++] = global::Foo.MyEnum.Third; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInGlobalNamespace.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInGlobalNamespace.verified.txt index 2208182..38501d7 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInGlobalNamespace.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInGlobalNamespace.verified.txt @@ -24,6 +24,11 @@ /// public const int Length = 3; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::MyEnum All = global::MyEnum.First | global::MyEnum.Second | global::MyEnum.Third; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -102,6 +107,52 @@ public static bool HasFlagFast(this global::MyEnum value, global::MyEnum flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::MyEnum value, global::MyEnum otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 3; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::MyEnum value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::MyEnum.First) == global::MyEnum.First) + buffer[count++] = global::MyEnum.First; + if ((value & global::MyEnum.Second) == global::MyEnum.Second) + buffer[count++] = global::MyEnum.Second; + if ((value & global::MyEnum.Third) == global::MyEnum.Third) + buffer[count++] = global::MyEnum.Third; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptHasFlag.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptHasFlag.verified.txt index 29cc515..2a130d4 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptHasFlag.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptHasFlag.verified.txt @@ -26,6 +26,11 @@ namespace MyTestNameSpace /// public const int Length = 3; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::MyTestNameSpace.MyEnum All = global::MyTestNameSpace.MyEnum.First | global::MyTestNameSpace.MyEnum.Second | global::MyTestNameSpace.MyEnum.Third; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -104,6 +109,52 @@ namespace MyTestNameSpace public static bool HasFlagFast(this global::MyTestNameSpace.MyEnum value, global::MyTestNameSpace.MyEnum flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::MyTestNameSpace.MyEnum value, global::MyTestNameSpace.MyEnum otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 3; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::MyTestNameSpace.MyEnum value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::MyTestNameSpace.MyEnum.First) == global::MyTestNameSpace.MyEnum.First) + buffer[count++] = global::MyTestNameSpace.MyEnum.First; + if ((value & global::MyTestNameSpace.MyEnum.Second) == global::MyTestNameSpace.MyEnum.Second) + buffer[count++] = global::MyTestNameSpace.MyEnum.Second; + if ((value & global::MyTestNameSpace.MyEnum.Third) == global::MyTestNameSpace.MyEnum.Third) + buffer[count++] = global::MyTestNameSpace.MyEnum.Third; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.DoesNotInterceptEnumMarkedAsNotInterceptable.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.DoesNotInterceptEnumMarkedAsNotInterceptable.verified.txt index 01973f1..3cc8238 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.DoesNotInterceptEnumMarkedAsNotInterceptable.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.DoesNotInterceptEnumMarkedAsNotInterceptable.verified.txt @@ -854,6 +854,11 @@ namespace MyTestNameSpace /// public const int Length = 3; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::MyTestNameSpace.AnotherEnum All = global::MyTestNameSpace.AnotherEnum.First | global::MyTestNameSpace.AnotherEnum.Second | global::MyTestNameSpace.AnotherEnum.Third; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -932,6 +937,50 @@ namespace MyTestNameSpace public static bool HasFlagFast(this global::MyTestNameSpace.AnotherEnum value, global::MyTestNameSpace.AnotherEnum flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::MyTestNameSpace.AnotherEnum value, global::MyTestNameSpace.AnotherEnum otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 2; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::MyTestNameSpace.AnotherEnum value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::MyTestNameSpace.AnotherEnum.Second) == global::MyTestNameSpace.AnotherEnum.Second) + buffer[count++] = global::MyTestNameSpace.AnotherEnum.Second; + if ((value & global::MyTestNameSpace.AnotherEnum.Third) == global::MyTestNameSpace.AnotherEnum.Third) + buffer[count++] = global::MyTestNameSpace.AnotherEnum.Third; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.DoesNotGenerateTryGetFlagsWhenMemberHasUndefinedBits_csharp14IsSupported=False.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.DoesNotGenerateTryGetFlagsWhenMemberHasUndefinedBits_csharp14IsSupported=False.verified.txt new file mode 100644 index 0000000..b1eb187 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.DoesNotGenerateTryGetFlagsWhenMemberHasUndefinedBits_csharp14IsSupported=False.verified.txt @@ -0,0 +1,866 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +namespace Something.Blah +{ +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums + /// + /// Extension methods for + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("NetEscapades.EnumGenerators", "FIXED_VERSION")] + public static partial class ShortName + { + /// + /// The number of members in the enum. + /// This is a non-distinct count of defined names. + /// + public const int Length = 4; + + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Something.Blah.ShortName All = global::Something.Blah.ShortName.None | global::Something.Blah.ShortName.First | global::Something.Blah.ShortName.Second | global::Something.Blah.ShortName.Odd; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The string representation of the value, the same as that returned by ToString() + public static string ToStringFast(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => nameof(global::Something.Blah.ShortName.None), + global::Something.Blah.ShortName.First => nameof(global::Something.Blah.ShortName.First), + global::Something.Blah.ShortName.Second => nameof(global::Something.Blah.ShortName.Second), + global::Something.Blah.ShortName.Odd => nameof(global::Something.Blah.ShortName.Odd), + _ => value.ToString(), + }; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The options to use when serializing the enum + /// The string representation of the value, using the provided options. +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static string ToStringFast(this global::Something.Blah.ShortName value, global::NetEscapades.EnumGenerators.SerializationOptions options) + => options.Transform switch + { + global::NetEscapades.EnumGenerators.SerializationTransform.LowerInvariant => value.ToStringFastLowerInvariant(), + global::NetEscapades.EnumGenerators.SerializationTransform.UpperInvariant => value.ToStringFastUpperInvariant(), + _ => value.ToStringFast(), + }; + + private static string ToStringFastLowerInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "none", + global::Something.Blah.ShortName.First => "first", + global::Something.Blah.ShortName.Second => "second", + global::Something.Blah.ShortName.Odd => "odd", + _ => value.ToString().ToLowerInvariant(), + }; + + private static string ToStringFastUpperInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "NONE", + global::Something.Blah.ShortName.First => "FIRST", + global::Something.Blah.ShortName.Second => "SECOND", + global::Something.Blah.ShortName.Odd => "ODD", + _ => value.ToString().ToUpperInvariant(), + }; + + /// + /// Returns the string representation of the value. + /// If the member is decorated with the attribute + /// then that value is returned. Otherwise returns uses the name of the member, + /// equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// If uses the value provided in the + /// attribute as the string representation of the member. + /// If , always uses the name of the member, the same as if ToString() was called. + /// The string representation of the value + public static string ToStringFast(this global::Something.Blah.ShortName value, bool useMetadataAttributes) + => value.ToStringFast(); + + /// + /// Determines whether one or more bit fields are set in the current instance. + /// Equivalent to calling on . + /// + /// The value of the instance to investigate + /// The flag to check for + /// if the fields set in the flag are also set in the current instance; otherwise . + /// If the underlying value of is zero, the method returns true. + /// This is consistent with the behaviour of + public static bool HasFlagFast(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName flag) + => flag == 0 ? true : (value & flag) == flag; + + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// Cast a value of to the underlying type (int). + /// This is mainly a convenience method. + /// + /// The value of cast to the underlying type. + public static int AsUnderlyingType(this global::Something.Blah.ShortName value) + { + return (int) value; + } + + /// + /// Returns a boolean telling whether the given enum value exists in the enumeration. + /// + /// The value to check if it's defined + /// if the value exists in the enumeration, otherwise + public static bool IsDefined(global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => true, + global::Something.Blah.ShortName.First => true, + global::Something.Blah.ShortName.Second => true, + global::Something.Blah.ShortName.Odd => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration. + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(string name) + => name switch + { + nameof(global::Something.Blah.ShortName.None) => true, + nameof(global::Something.Blah.ShortName.First) => true, + nameof(global::Something.Blah.ShortName.Second) => true, + nameof(global::Something.Blah.ShortName.Odd) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or if a member decorated with + /// with the required name exists. + /// + /// The name to check if it's defined + /// If , + /// considers the value of + /// instead of the member name, otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + => IsDefined(name); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name) + => name switch + { + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Odd)), global::System.StringComparison.Ordinal) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or optionally if a member decorated with + /// exists. Slower then the overload, but doesn't allocate memory./> + /// + /// The name to check if it's defined + /// If , considers the value of metadata attributes,otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name, bool allowMatchingMetadataAttribute) + => IsDefined(name); +#endif + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name) + => TryParse(name, out var value, new global::NetEscapades.EnumGenerators.EnumParseOptions()) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(string? name) + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value) + => TryParse(name, out value, new global::NetEscapades.EnumGenerators.EnumParseOptions()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse(name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , + /// considers the value included in attribute + /// when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case string s when s.Equals(nameof(global::Something.Blah.ShortName.None), options.ComparisonType): + value = global::Something.Blah.ShortName.None; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.First), options.ComparisonType): + value = global::Something.Blah.ShortName.First; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Second), options.ComparisonType): + value = global::Something.Blah.ShortName.Second; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Odd), options.ComparisonType): + value = global::Something.Blah.ShortName.Odd; + return true; + case string s when options.EnableNumberParsing && int.TryParse(name, out var val): + value = (global::Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(in global::System.ReadOnlySpan name) +#if NET6_0_OR_GREATER + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); +#else + => throw new global::System.ArgumentException($"Requested value '{name.ToString()}' was not found."); +#endif + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out result, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static bool TryParse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), options.ComparisonType): + result = global::Something.Blah.ShortName.None; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), options.ComparisonType): + result = global::Something.Blah.ShortName.First; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), options.ComparisonType): + result = global::Something.Blah.ShortName.Second; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Odd)), options.ComparisonType): + result = global::Something.Blah.ShortName.Odd; + return true; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name, out var numericResult): +#else + // TryParse with ReadOnlySpan is not available, so we have to call ToString() + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name.ToString(), out var numericResult): +#endif + result = (global::Something.Blah.ShortName)numericResult; + return true; + default: + result = default; + return false; + } + } +#endif + + /// + /// Retrieves an array of the values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the values defined in + public static global::Something.Blah.ShortName[] GetValues() + => new[] + { + global::Something.Blah.ShortName.None, + global::Something.Blah.ShortName.First, + global::Something.Blah.ShortName.Second, + global::Something.Blah.ShortName.Odd, + }; + + /// + /// Retrieves an array of the underlying-values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the underlying-values defined in + public static int[] GetValuesAsUnderlyingType() + => new[] + { + (int) global::Something.Blah.ShortName.None, + (int) global::Something.Blah.ShortName.First, + (int) global::Something.Blah.ShortName.Second, + (int) global::Something.Blah.ShortName.Odd, + }; + + /// + /// Retrieves an array of the names of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the names of the members defined in + public static string[] GetNames() + => new[] + { + nameof(global::Something.Blah.ShortName.None), + nameof(global::Something.Blah.ShortName.First), + nameof(global::Something.Blah.ShortName.Second), + nameof(global::Something.Blah.ShortName.Odd), + }; + } +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.DoesNotGenerateTryGetFlagsWhenMemberHasUndefinedBits_csharp14IsSupported=True.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.DoesNotGenerateTryGetFlagsWhenMemberHasUndefinedBits_csharp14IsSupported=True.verified.txt new file mode 100644 index 0000000..d9ce603 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.DoesNotGenerateTryGetFlagsWhenMemberHasUndefinedBits_csharp14IsSupported=True.verified.txt @@ -0,0 +1,871 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +namespace Something.Blah +{ +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums + /// + /// Extension methods for + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("NetEscapades.EnumGenerators", "FIXED_VERSION")] + public static partial class ShortName + { + /// + /// The number of members in the enum. + /// This is a non-distinct count of defined names. + /// + public const int Length = 4; + + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Something.Blah.ShortName All = global::Something.Blah.ShortName.None | global::Something.Blah.ShortName.First | global::Something.Blah.ShortName.Second | global::Something.Blah.ShortName.Odd; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The string representation of the value, the same as that returned by ToString() + public static string ToStringFast(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => nameof(global::Something.Blah.ShortName.None), + global::Something.Blah.ShortName.First => nameof(global::Something.Blah.ShortName.First), + global::Something.Blah.ShortName.Second => nameof(global::Something.Blah.ShortName.Second), + global::Something.Blah.ShortName.Odd => nameof(global::Something.Blah.ShortName.Odd), + _ => value.ToString(), + }; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The options to use when serializing the enum + /// The string representation of the value, using the provided options. +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static string ToStringFast(this global::Something.Blah.ShortName value, global::NetEscapades.EnumGenerators.SerializationOptions options) + => options.Transform switch + { + global::NetEscapades.EnumGenerators.SerializationTransform.LowerInvariant => value.ToStringFastLowerInvariant(), + global::NetEscapades.EnumGenerators.SerializationTransform.UpperInvariant => value.ToStringFastUpperInvariant(), + _ => value.ToStringFast(), + }; + + private static string ToStringFastLowerInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "none", + global::Something.Blah.ShortName.First => "first", + global::Something.Blah.ShortName.Second => "second", + global::Something.Blah.ShortName.Odd => "odd", + _ => value.ToString().ToLowerInvariant(), + }; + + private static string ToStringFastUpperInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "NONE", + global::Something.Blah.ShortName.First => "FIRST", + global::Something.Blah.ShortName.Second => "SECOND", + global::Something.Blah.ShortName.Odd => "ODD", + _ => value.ToString().ToUpperInvariant(), + }; + + /// + /// Returns the string representation of the value. + /// If the member is decorated with the attribute + /// then that value is returned. Otherwise returns uses the name of the member, + /// equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// If uses the value provided in the + /// attribute as the string representation of the member. + /// If , always uses the name of the member, the same as if ToString() was called. + /// The string representation of the value + public static string ToStringFast(this global::Something.Blah.ShortName value, bool useMetadataAttributes) + => value.ToStringFast(); + + /// + /// Determines whether one or more bit fields are set in the current instance. + /// Equivalent to calling on . + /// + /// The value of the instance to investigate + /// The flag to check for + /// if the fields set in the flag are also set in the current instance; otherwise . + /// If the underlying value of is zero, the method returns true. + /// This is consistent with the behaviour of + public static bool HasFlagFast(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName flag) + => flag == 0 ? true : (value & flag) == flag; + + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// Cast a value of to the underlying type (int). + /// This is mainly a convenience method. + /// + /// The value of cast to the underlying type. + public static int AsUnderlyingType(this global::Something.Blah.ShortName value) + { + return (int) value; + } + + // C#14 Extension member syntax + extension(global::Something.Blah.ShortName) + { + + /// + /// Returns a boolean telling whether the given enum value exists in the enumeration. + /// + /// The value to check if it's defined + /// if the value exists in the enumeration, otherwise + public static bool IsDefined(global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => true, + global::Something.Blah.ShortName.First => true, + global::Something.Blah.ShortName.Second => true, + global::Something.Blah.ShortName.Odd => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration. + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(string name) + => name switch + { + nameof(global::Something.Blah.ShortName.None) => true, + nameof(global::Something.Blah.ShortName.First) => true, + nameof(global::Something.Blah.ShortName.Second) => true, + nameof(global::Something.Blah.ShortName.Odd) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or if a member decorated with + /// with the required name exists. + /// + /// The name to check if it's defined + /// If , + /// considers the value of + /// instead of the member name, otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + => IsDefined(name); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name) + => name switch + { + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Odd)), global::System.StringComparison.Ordinal) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or optionally if a member decorated with + /// exists. Slower then the overload, but doesn't allocate memory./> + /// + /// The name to check if it's defined + /// If , considers the value of metadata attributes,otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name, bool allowMatchingMetadataAttribute) + => IsDefined(name); +#endif + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name) + => TryParse(name, out var value, new global::NetEscapades.EnumGenerators.EnumParseOptions()) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(string? name) + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value) + => TryParse(name, out value, new global::NetEscapades.EnumGenerators.EnumParseOptions()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse(name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , + /// considers the value included in attribute + /// when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case string s when s.Equals(nameof(global::Something.Blah.ShortName.None), options.ComparisonType): + value = global::Something.Blah.ShortName.None; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.First), options.ComparisonType): + value = global::Something.Blah.ShortName.First; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Second), options.ComparisonType): + value = global::Something.Blah.ShortName.Second; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Odd), options.ComparisonType): + value = global::Something.Blah.ShortName.Odd; + return true; + case string s when options.EnableNumberParsing && int.TryParse(name, out var val): + value = (global::Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(in global::System.ReadOnlySpan name) +#if NET6_0_OR_GREATER + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); +#else + => throw new global::System.ArgumentException($"Requested value '{name.ToString()}' was not found."); +#endif + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out result, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static bool TryParse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), options.ComparisonType): + result = global::Something.Blah.ShortName.None; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), options.ComparisonType): + result = global::Something.Blah.ShortName.First; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), options.ComparisonType): + result = global::Something.Blah.ShortName.Second; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Odd)), options.ComparisonType): + result = global::Something.Blah.ShortName.Odd; + return true; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name, out var numericResult): +#else + // TryParse with ReadOnlySpan is not available, so we have to call ToString() + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name.ToString(), out var numericResult): +#endif + result = (global::Something.Blah.ShortName)numericResult; + return true; + default: + result = default; + return false; + } + } +#endif + + /// + /// Retrieves an array of the values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the values defined in + public static global::Something.Blah.ShortName[] GetValues() + => new[] + { + global::Something.Blah.ShortName.None, + global::Something.Blah.ShortName.First, + global::Something.Blah.ShortName.Second, + global::Something.Blah.ShortName.Odd, + }; + + /// + /// Retrieves an array of the underlying-values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the underlying-values defined in + public static int[] GetValuesAsUnderlyingType() + => new[] + { + (int) global::Something.Blah.ShortName.None, + (int) global::Something.Blah.ShortName.First, + (int) global::Something.Blah.ShortName.Second, + (int) global::Something.Blah.ShortName.Odd, + }; + + /// + /// Retrieves an array of the names of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the names of the members defined in + public static string[] GetNames() + => new[] + { + nameof(global::Something.Blah.ShortName.None), + nameof(global::Something.Blah.ShortName.First), + nameof(global::Something.Blah.ShortName.Second), + nameof(global::Something.Blah.ShortName.Odd), + }; + } + } +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=False.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=False.verified.txt index 39a725f..3f5cafe 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=False.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=False.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -25,6 +25,11 @@ namespace Something.Blah /// public const int Length = 2; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Something.Blah.ShortName All = global::Something.Blah.ShortName.First | global::Something.Blah.ShortName.Second; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -100,6 +105,48 @@ namespace Something.Blah public static bool HasFlagFast(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 1; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::Something.Blah.ShortName value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::Something.Blah.ShortName.Second) == global::Something.Blah.ShortName.Second) + buffer[count++] = global::Something.Blah.ShortName.Second; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=True.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=True.verified.txt index 215c19c..fac82ea 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=True.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly_csharp14IsSupported=True.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -25,6 +25,11 @@ namespace Something.Blah /// public const int Length = 2; + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Something.Blah.ShortName All = global::Something.Blah.ShortName.First | global::Something.Blah.ShortName.Second; + /// /// Returns the string representation of the value. /// Directly equivalent to calling ToString() on . @@ -100,6 +105,48 @@ namespace Something.Blah public static bool HasFlagFast(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName flag) => flag == 0 ? true : (value & flag) == flag; + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 1; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::Something.Blah.ShortName value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::Something.Blah.ShortName.Second) == global::Something.Blah.ShortName.Second) + buffer[count++] = global::Something.Blah.ShortName.Second; + return true; + } +#endif + /// /// Cast a value of to the underlying type (int). /// This is mainly a convenience method. diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesTryGetFlagsWithCleanCompositeMember_csharp14IsSupported=False.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesTryGetFlagsWithCleanCompositeMember_csharp14IsSupported=False.verified.txt new file mode 100644 index 0000000..ca11283 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesTryGetFlagsWithCleanCompositeMember_csharp14IsSupported=False.verified.txt @@ -0,0 +1,898 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +namespace Something.Blah +{ +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums + /// + /// Extension methods for + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("NetEscapades.EnumGenerators", "FIXED_VERSION")] + public static partial class ShortName + { + /// + /// The number of members in the enum. + /// This is a non-distinct count of defined names. + /// + public const int Length = 4; + + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Something.Blah.ShortName All = global::Something.Blah.ShortName.None | global::Something.Blah.ShortName.First | global::Something.Blah.ShortName.Second | global::Something.Blah.ShortName.Both; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The string representation of the value, the same as that returned by ToString() + public static string ToStringFast(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => nameof(global::Something.Blah.ShortName.None), + global::Something.Blah.ShortName.First => nameof(global::Something.Blah.ShortName.First), + global::Something.Blah.ShortName.Second => nameof(global::Something.Blah.ShortName.Second), + global::Something.Blah.ShortName.Both => nameof(global::Something.Blah.ShortName.Both), + _ => value.ToString(), + }; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The options to use when serializing the enum + /// The string representation of the value, using the provided options. +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static string ToStringFast(this global::Something.Blah.ShortName value, global::NetEscapades.EnumGenerators.SerializationOptions options) + => options.Transform switch + { + global::NetEscapades.EnumGenerators.SerializationTransform.LowerInvariant => value.ToStringFastLowerInvariant(), + global::NetEscapades.EnumGenerators.SerializationTransform.UpperInvariant => value.ToStringFastUpperInvariant(), + _ => value.ToStringFast(), + }; + + private static string ToStringFastLowerInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "none", + global::Something.Blah.ShortName.First => "first", + global::Something.Blah.ShortName.Second => "second", + global::Something.Blah.ShortName.Both => "both", + _ => value.ToString().ToLowerInvariant(), + }; + + private static string ToStringFastUpperInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "NONE", + global::Something.Blah.ShortName.First => "FIRST", + global::Something.Blah.ShortName.Second => "SECOND", + global::Something.Blah.ShortName.Both => "BOTH", + _ => value.ToString().ToUpperInvariant(), + }; + + /// + /// Returns the string representation of the value. + /// If the member is decorated with the attribute + /// then that value is returned. Otherwise returns uses the name of the member, + /// equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// If uses the value provided in the + /// attribute as the string representation of the member. + /// If , always uses the name of the member, the same as if ToString() was called. + /// The string representation of the value + public static string ToStringFast(this global::Something.Blah.ShortName value, bool useMetadataAttributes) + => value.ToStringFast(); + + /// + /// Determines whether one or more bit fields are set in the current instance. + /// Equivalent to calling on . + /// + /// The value of the instance to investigate + /// The flag to check for + /// if the fields set in the flag are also set in the current instance; otherwise . + /// If the underlying value of is zero, the method returns true. + /// This is consistent with the behaviour of + public static bool HasFlagFast(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName flag) + => flag == 0 ? true : (value & flag) == flag; + + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 2; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::Something.Blah.ShortName value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::Something.Blah.ShortName.First) == global::Something.Blah.ShortName.First) + buffer[count++] = global::Something.Blah.ShortName.First; + if ((value & global::Something.Blah.ShortName.Second) == global::Something.Blah.ShortName.Second) + buffer[count++] = global::Something.Blah.ShortName.Second; + return true; + } +#endif + + /// + /// Cast a value of to the underlying type (int). + /// This is mainly a convenience method. + /// + /// The value of cast to the underlying type. + public static int AsUnderlyingType(this global::Something.Blah.ShortName value) + { + return (int) value; + } + + /// + /// Returns a boolean telling whether the given enum value exists in the enumeration. + /// + /// The value to check if it's defined + /// if the value exists in the enumeration, otherwise + public static bool IsDefined(global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => true, + global::Something.Blah.ShortName.First => true, + global::Something.Blah.ShortName.Second => true, + global::Something.Blah.ShortName.Both => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration. + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(string name) + => name switch + { + nameof(global::Something.Blah.ShortName.None) => true, + nameof(global::Something.Blah.ShortName.First) => true, + nameof(global::Something.Blah.ShortName.Second) => true, + nameof(global::Something.Blah.ShortName.Both) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or if a member decorated with + /// with the required name exists. + /// + /// The name to check if it's defined + /// If , + /// considers the value of + /// instead of the member name, otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + => IsDefined(name); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name) + => name switch + { + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Both)), global::System.StringComparison.Ordinal) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or optionally if a member decorated with + /// exists. Slower then the overload, but doesn't allocate memory./> + /// + /// The name to check if it's defined + /// If , considers the value of metadata attributes,otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name, bool allowMatchingMetadataAttribute) + => IsDefined(name); +#endif + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name) + => TryParse(name, out var value, new global::NetEscapades.EnumGenerators.EnumParseOptions()) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(string? name) + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value) + => TryParse(name, out value, new global::NetEscapades.EnumGenerators.EnumParseOptions()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse(name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , + /// considers the value included in attribute + /// when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case string s when s.Equals(nameof(global::Something.Blah.ShortName.None), options.ComparisonType): + value = global::Something.Blah.ShortName.None; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.First), options.ComparisonType): + value = global::Something.Blah.ShortName.First; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Second), options.ComparisonType): + value = global::Something.Blah.ShortName.Second; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Both), options.ComparisonType): + value = global::Something.Blah.ShortName.Both; + return true; + case string s when options.EnableNumberParsing && int.TryParse(name, out var val): + value = (global::Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(in global::System.ReadOnlySpan name) +#if NET6_0_OR_GREATER + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); +#else + => throw new global::System.ArgumentException($"Requested value '{name.ToString()}' was not found."); +#endif + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out result, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static bool TryParse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), options.ComparisonType): + result = global::Something.Blah.ShortName.None; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), options.ComparisonType): + result = global::Something.Blah.ShortName.First; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), options.ComparisonType): + result = global::Something.Blah.ShortName.Second; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Both)), options.ComparisonType): + result = global::Something.Blah.ShortName.Both; + return true; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name, out var numericResult): +#else + // TryParse with ReadOnlySpan is not available, so we have to call ToString() + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name.ToString(), out var numericResult): +#endif + result = (global::Something.Blah.ShortName)numericResult; + return true; + default: + result = default; + return false; + } + } +#endif + + /// + /// Retrieves an array of the values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the values defined in + public static global::Something.Blah.ShortName[] GetValues() + => new[] + { + global::Something.Blah.ShortName.None, + global::Something.Blah.ShortName.First, + global::Something.Blah.ShortName.Second, + global::Something.Blah.ShortName.Both, + }; + + /// + /// Retrieves an array of the underlying-values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the underlying-values defined in + public static int[] GetValuesAsUnderlyingType() + => new[] + { + (int) global::Something.Blah.ShortName.None, + (int) global::Something.Blah.ShortName.First, + (int) global::Something.Blah.ShortName.Second, + (int) global::Something.Blah.ShortName.Both, + }; + + /// + /// Retrieves an array of the names of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the names of the members defined in + public static string[] GetNames() + => new[] + { + nameof(global::Something.Blah.ShortName.None), + nameof(global::Something.Blah.ShortName.First), + nameof(global::Something.Blah.ShortName.Second), + nameof(global::Something.Blah.ShortName.Both), + }; + } +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesTryGetFlagsWithCleanCompositeMember_csharp14IsSupported=True.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesTryGetFlagsWithCleanCompositeMember_csharp14IsSupported=True.verified.txt new file mode 100644 index 0000000..ebb6bc0 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesTryGetFlagsWithCleanCompositeMember_csharp14IsSupported=True.verified.txt @@ -0,0 +1,903 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +namespace Something.Blah +{ +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums + /// + /// Extension methods for + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("NetEscapades.EnumGenerators", "FIXED_VERSION")] + public static partial class ShortName + { + /// + /// The number of members in the enum. + /// This is a non-distinct count of defined names. + /// + public const int Length = 4; + + /// + /// A bitwise OR combination of all defined values in the enum. + /// + public const global::Something.Blah.ShortName All = global::Something.Blah.ShortName.None | global::Something.Blah.ShortName.First | global::Something.Blah.ShortName.Second | global::Something.Blah.ShortName.Both; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The string representation of the value, the same as that returned by ToString() + public static string ToStringFast(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => nameof(global::Something.Blah.ShortName.None), + global::Something.Blah.ShortName.First => nameof(global::Something.Blah.ShortName.First), + global::Something.Blah.ShortName.Second => nameof(global::Something.Blah.ShortName.Second), + global::Something.Blah.ShortName.Both => nameof(global::Something.Blah.ShortName.Both), + _ => value.ToString(), + }; + + /// + /// Returns the string representation of the value. + /// Directly equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// The options to use when serializing the enum + /// The string representation of the value, using the provided options. +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static string ToStringFast(this global::Something.Blah.ShortName value, global::NetEscapades.EnumGenerators.SerializationOptions options) + => options.Transform switch + { + global::NetEscapades.EnumGenerators.SerializationTransform.LowerInvariant => value.ToStringFastLowerInvariant(), + global::NetEscapades.EnumGenerators.SerializationTransform.UpperInvariant => value.ToStringFastUpperInvariant(), + _ => value.ToStringFast(), + }; + + private static string ToStringFastLowerInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "none", + global::Something.Blah.ShortName.First => "first", + global::Something.Blah.ShortName.Second => "second", + global::Something.Blah.ShortName.Both => "both", + _ => value.ToString().ToLowerInvariant(), + }; + + private static string ToStringFastUpperInvariant(this global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => "NONE", + global::Something.Blah.ShortName.First => "FIRST", + global::Something.Blah.ShortName.Second => "SECOND", + global::Something.Blah.ShortName.Both => "BOTH", + _ => value.ToString().ToUpperInvariant(), + }; + + /// + /// Returns the string representation of the value. + /// If the member is decorated with the attribute + /// then that value is returned. Otherwise returns uses the name of the member, + /// equivalent to calling ToString() on . + /// + /// The value to retrieve the string value for + /// If uses the value provided in the + /// attribute as the string representation of the member. + /// If , always uses the name of the member, the same as if ToString() was called. + /// The string representation of the value + public static string ToStringFast(this global::Something.Blah.ShortName value, bool useMetadataAttributes) + => value.ToStringFast(); + + /// + /// Determines whether one or more bit fields are set in the current instance. + /// Equivalent to calling on . + /// + /// The value of the instance to investigate + /// The flag to check for + /// if the fields set in the flag are also set in the current instance; otherwise . + /// If the underlying value of is zero, the method returns true. + /// This is consistent with the behaviour of + public static bool HasFlagFast(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName flag) + => flag == 0 ? true : (value & flag) == flag; + + /// + /// Determines whether any of the bit fields are set in the current instance. + /// + /// The value of the instance to investigate + /// The flags to check for + /// if any of the fields set in are + /// also set in ; otherwise . + /// If the underlying value of is zero, the method + /// always returns , consistent with the behaviour of . + public static bool HasAnyFlags(this global::Something.Blah.ShortName value, global::Something.Blah.ShortName otherFlags) + => otherFlags == 0 ? true : (value & otherFlags) != 0; + + /// + /// The number of distinct single-bit flag members defined in the enum. + /// This is the buffer size required by . + /// + public const int DistinctFlagCount = 2; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Writes each defined single-bit flag set in into . + /// Composite members (e.g. a member defined as A | B) are decomposed into their constituent single-bit flags. + /// The zero/None value never appears in the output. + /// + /// The value to decompose into individual flags. + /// The buffer to write the flags into. Must be at least elements. + /// The number of flags written to . Set to 0 if the method returns . + /// if the buffer was large enough; if it was smaller than . + public static bool TryGetFlags(this global::Something.Blah.ShortName value, global::System.Span buffer, out int count) + { + if (buffer.Length < DistinctFlagCount) + { + count = 0; + return false; + } + count = 0; + if ((value & global::Something.Blah.ShortName.First) == global::Something.Blah.ShortName.First) + buffer[count++] = global::Something.Blah.ShortName.First; + if ((value & global::Something.Blah.ShortName.Second) == global::Something.Blah.ShortName.Second) + buffer[count++] = global::Something.Blah.ShortName.Second; + return true; + } +#endif + + /// + /// Cast a value of to the underlying type (int). + /// This is mainly a convenience method. + /// + /// The value of cast to the underlying type. + public static int AsUnderlyingType(this global::Something.Blah.ShortName value) + { + return (int) value; + } + + // C#14 Extension member syntax + extension(global::Something.Blah.ShortName) + { + + /// + /// Returns a boolean telling whether the given enum value exists in the enumeration. + /// + /// The value to check if it's defined + /// if the value exists in the enumeration, otherwise + public static bool IsDefined(global::Something.Blah.ShortName value) + => value switch + { + global::Something.Blah.ShortName.None => true, + global::Something.Blah.ShortName.First => true, + global::Something.Blah.ShortName.Second => true, + global::Something.Blah.ShortName.Both => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration. + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(string name) + => name switch + { + nameof(global::Something.Blah.ShortName.None) => true, + nameof(global::Something.Blah.ShortName.First) => true, + nameof(global::Something.Blah.ShortName.Second) => true, + nameof(global::Something.Blah.ShortName.Both) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or if a member decorated with + /// with the required name exists. + /// + /// The name to check if it's defined + /// If , + /// considers the value of + /// instead of the member name, otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + => IsDefined(name); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration + /// + /// The name to check if it's defined + /// if a member with the name exists in the enumeration, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name) + => name switch + { + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), global::System.StringComparison.Ordinal) => true, + global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Both)), global::System.StringComparison.Ordinal) => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or optionally if a member decorated with + /// exists. Slower then the overload, but doesn't allocate memory./> + /// + /// The name to check if it's defined + /// If , considers the value of metadata attributes,otherwise ignores them + /// if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, otherwise + public static bool IsDefined(in global::System.ReadOnlySpan name, bool allowMatchingMetadataAttribute) + => IsDefined(name); +#endif + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name) + => TryParse(name, out var value, new global::NetEscapades.EnumGenerators.EnumParseOptions()) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(string? name) + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value) + => TryParse(name, out value, new global::NetEscapades.EnumGenerators.EnumParseOptions()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse(name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , + /// considers the value included in attribute + /// when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Something.Blah.ShortName value, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case string s when s.Equals(nameof(global::Something.Blah.ShortName.None), options.ComparisonType): + value = global::Something.Blah.ShortName.None; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.First), options.ComparisonType): + value = global::Something.Blah.ShortName.First; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Second), options.ComparisonType): + value = global::Something.Blah.ShortName.Second; + return true; + case string s when s.Equals(nameof(global::Something.Blah.ShortName.Both), options.ComparisonType): + value = global::Something.Blah.ShortName.Both; + return true; + case string s when options.EnableNumberParsing && int.TryParse(name, out var val): + value = (global::Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out var value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)) + ? value : ThrowValueNotFound(name); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static global::Something.Blah.ShortName Parse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The string representation of the enumeration name or underlying value to convert + /// Options that control how the string value should be parsed. + /// An object of type whose + /// value is represented by +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static global::Something.Blah.ShortName Parse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + => TryParse( + name, + out var value, + options) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Something.Blah.ShortName ThrowValueNotFound(in global::System.ReadOnlySpan name) +#if NET6_0_OR_GREATER + => throw new global::System.ArgumentException($"Requested value '{name}' was not found."); +#else + => throw new global::System.ArgumentException($"Requested value '{name.ToString()}' was not found."); +#endif + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions()); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName value, + bool ignoreCase) + => TryParse( + name, + out value, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, call the + /// overload, and disable number parsing. + public static bool TryParse( +#else + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// to read value in case insensitive mode; to read value in case sensitive mode. + /// If , considers the value included in + /// attribute when parsing, otherwise only considers the member names. + /// if the value parameter was converted successfully; otherwise, . + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse( + name, + out result, + new global::NetEscapades.EnumGenerators.EnumParseOptions( + ignoreCase ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal, + allowMatchingMetadataAttribute: allowMatchingMetadataAttribute)); + +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && NETESCAPADES_ENUMGENERATORS_SYSTEM_MEMORY + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . + /// WARNING: This API will allocate a when + /// is not explicitly defined in when attempting to parse as a number. + /// To avoid this allocation, only call this overload when you know the value will exist. + /// Alternatively, disable number parsing in the parameter. + +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + public static bool TryParse( +#else + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// Options that control how the string value should be parsed. + /// if the value parameter was converted successfully; otherwise, . +#if !NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY + [global::System.Runtime.CompilerServices.OverloadResolutionPriority(1)] +#endif + + public static bool TryParse( +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in global::System.ReadOnlySpan name, + out global::Something.Blah.ShortName result, + global::NetEscapades.EnumGenerators.EnumParseOptions options) + { + switch (name) + { + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.None)), options.ComparisonType): + result = global::Something.Blah.ShortName.None; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.First)), options.ComparisonType): + result = global::Something.Blah.ShortName.First; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Second)), options.ComparisonType): + result = global::Something.Blah.ShortName.Second; + return true; + case global::System.ReadOnlySpan current when global::System.MemoryExtensions.Equals(current, global::System.MemoryExtensions.AsSpan(nameof(global::Something.Blah.ShortName.Both)), options.ComparisonType): + result = global::Something.Blah.ShortName.Both; + return true; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name, out var numericResult): +#else + // TryParse with ReadOnlySpan is not available, so we have to call ToString() + case global::System.ReadOnlySpan current when options.EnableNumberParsing && int.TryParse(name.ToString(), out var numericResult): +#endif + result = (global::Something.Blah.ShortName)numericResult; + return true; + default: + result = default; + return false; + } + } +#endif + + /// + /// Retrieves an array of the values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the values defined in + public static global::Something.Blah.ShortName[] GetValues() + => new[] + { + global::Something.Blah.ShortName.None, + global::Something.Blah.ShortName.First, + global::Something.Blah.ShortName.Second, + global::Something.Blah.ShortName.Both, + }; + + /// + /// Retrieves an array of the underlying-values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the underlying-values defined in + public static int[] GetValuesAsUnderlyingType() + => new[] + { + (int) global::Something.Blah.ShortName.None, + (int) global::Something.Blah.ShortName.First, + (int) global::Something.Blah.ShortName.Second, + (int) global::Something.Blah.ShortName.Both, + }; + + /// + /// Retrieves an array of the names of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the names of the members defined in + public static string[] GetNames() + => new[] + { + nameof(global::Something.Blah.ShortName.None), + nameof(global::Something.Blah.ShortName.First), + nameof(global::Something.Blah.ShortName.Second), + nameof(global::Something.Blah.ShortName.Both), + }; + } + } +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs b/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs index d5ac5c9..f86f201 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs +++ b/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs @@ -116,6 +116,79 @@ public Task GeneratesFlagsEnumCorrectly(bool csharp14IsSupported) .UseParameters(csharp14IsSupported); } + [Theory] + [CombinatorialData] + public Task DoesNotGenerateTryGetFlagsWhenMemberHasUndefinedBits(bool csharp14IsSupported) + { + // A member whose value contains a bit not corresponding to any single-bit flag disqualifies emission. + // Here Odd=5 = 1|4, but 4 is not defined as a single-bit flag, so no TryGetFlags is generated. + var value = new EnumToGenerate( + "ShortName", + "Something.Blah", + "Something.Blah.ShortName", + "int", + isPublic: true, + new List<(string, EnumValueOption)> + { + ("None", EnumValueOption.CreateWithoutAttributes(0)), + ("First", EnumValueOption.CreateWithoutAttributes(1)), + ("Second", EnumValueOption.CreateWithoutAttributes(2)), + ("Odd", EnumValueOption.CreateWithoutAttributes(5)), + }, + hasFlags: true, + metadataSource: null); + + var result = SourceGenerationHelper.GenerateExtensionClass( + value, + csharp14IsSupported, + useCollectionExpressions: false, + DefaultMetadataSource, + hasRuntimeDependencies: true, + forceInternal: false, + hasOverloadResolutionPriority: true).Content; + + return Verifier.Verify(result) + .ScrubExpectedChanges() + .UseDirectory("Snapshots") + .UseParameters(csharp14IsSupported); + } + + [Theory] + [CombinatorialData] + public Task GeneratesTryGetFlagsWithCleanCompositeMember(bool csharp14IsSupported) + { + // A composite member that's a clean combination of single-bit flags is fine; TryGetFlags is emitted. + var value = new EnumToGenerate( + "ShortName", + "Something.Blah", + "Something.Blah.ShortName", + "int", + isPublic: true, + new List<(string, EnumValueOption)> + { + ("None", EnumValueOption.CreateWithoutAttributes(0)), + ("First", EnumValueOption.CreateWithoutAttributes(1)), + ("Second", EnumValueOption.CreateWithoutAttributes(2)), + ("Both", EnumValueOption.CreateWithoutAttributes(3)), + }, + hasFlags: true, + metadataSource: null); + + var result = SourceGenerationHelper.GenerateExtensionClass( + value, + csharp14IsSupported, + useCollectionExpressions: false, + DefaultMetadataSource, + hasRuntimeDependencies: true, + forceInternal: false, + hasOverloadResolutionPriority: true).Content; + + return Verifier.Verify(result) + .ScrubExpectedChanges() + .UseDirectory("Snapshots") + .UseParameters(csharp14IsSupported); + } + [Fact] public Task GeneratesForcedInternalEnumCorrectly() {