diff --git a/README.md b/README.md index e06af5f..6102417 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ To use the generator, add the `[EnumExtensions]` attribute to an enum. For examp public enum MyEnum { First, + + [Display(Name = "2nd")] Second, } ``` @@ -56,7 +58,7 @@ public static partial class MyEnumExtensions => value switch { MyEnum.First => nameof(MyEnum.First), - MyEnum.Second => nameof(MyEnum.Second), + MyEnum.Second => "2nd", _ => value.ToString(), }; diff --git a/src/NetEscapades.EnumGenerators/EnumGenerator.cs b/src/NetEscapades.EnumGenerators/EnumGenerator.cs index 7d3c726..e21c3c3 100644 --- a/src/NetEscapades.EnumGenerators/EnumGenerator.cs +++ b/src/NetEscapades.EnumGenerators/EnumGenerator.cs @@ -149,7 +149,9 @@ static List GetTypesToGenerate(Compilation compilation, IEnumera string underlyingType = enumSymbol.EnumUnderlyingType?.ToString() ?? "int"; var enumMembers = enumSymbol.GetMembers(); - var members = new List>(enumMembers.Length); + var members = new List>(enumMembers.Length); + var displayNames = new HashSet(); + var isDisplayNameTheFirstPresence = false; foreach (var member in enumMembers) { @@ -159,7 +161,26 @@ static List GetTypesToGenerate(Compilation compilation, IEnumera continue; } - members.Add(new KeyValuePair(member.Name, field.ConstantValue)); + string? displayName = null; + foreach (var attribute in member.GetAttributes()) + { + if (attribute.AttributeClass is null || attribute.AttributeClass.Name != "DisplayAttribute") + { + continue; + } + + foreach (var namedArgument in attribute.NamedArguments) + { + if (namedArgument.Key == "Name" && namedArgument.Value.Value?.ToString() is { } dn) + { + displayName = dn; + isDisplayNameTheFirstPresence = displayNames.Add(displayName); + break; + } + } + } + + members.Add(new KeyValuePair(member.Name, new EnumValueOption(displayName, isDisplayNameTheFirstPresence))); } enumsToGenerate.Add(new EnumToGenerate( @@ -169,7 +190,8 @@ static List GetTypesToGenerate(Compilation compilation, IEnumera underlyingType: underlyingType, isPublic: enumSymbol.DeclaredAccessibility == Accessibility.Public, hasFlags: hasFlags, - values: members)); + names: members, + isDisplaAttributeUsed: displayNames.Count > 0)); } return enumsToGenerate; diff --git a/src/NetEscapades.EnumGenerators/EnumToGenerate.cs b/src/NetEscapades.EnumGenerators/EnumToGenerate.cs index ebede19..1ff2f76 100644 --- a/src/NetEscapades.EnumGenerators/EnumToGenerate.cs +++ b/src/NetEscapades.EnumGenerators/EnumToGenerate.cs @@ -8,7 +8,13 @@ public readonly struct EnumToGenerate public readonly bool IsPublic; public readonly bool HasFlags; public readonly string UnderlyingType; - public readonly List> Values; + + /// + /// Key is the enum name. + /// + public readonly List> Names; + + public readonly bool IsDisplaAttributeUsed; public EnumToGenerate( string name, @@ -16,15 +22,17 @@ public EnumToGenerate( string fullyQualifiedName, string underlyingType, bool isPublic, - List> values, - bool hasFlags) + List> names, + bool hasFlags, + bool isDisplaAttributeUsed) { Name = name; Namespace = ns; UnderlyingType = underlyingType; - Values = values; + Names = names; HasFlags = hasFlags; IsPublic = isPublic; FullyQualifiedName = fullyQualifiedName; + IsDisplaAttributeUsed = isDisplaAttributeUsed; } } \ No newline at end of file diff --git a/src/NetEscapades.EnumGenerators/EnumValueOption.cs b/src/NetEscapades.EnumGenerators/EnumValueOption.cs new file mode 100644 index 0000000..90c7647 --- /dev/null +++ b/src/NetEscapades.EnumGenerators/EnumValueOption.cs @@ -0,0 +1,16 @@ +namespace NetEscapades.EnumGenerators; + +public readonly struct EnumValueOption +{ + /// + /// Custom name setted by the [Display(Name)] attribute. + /// + public string? DisplayName { get; } + public bool IsDisplayNameTheFirstPresence { get; } + + public EnumValueOption(string? displayName, bool isDisplayNameTheFirstPresence) + { + DisplayName = displayName; + IsDisplayNameTheFirstPresence = isDisplayNameTheFirstPresence; + } +} diff --git a/src/NetEscapades.EnumGenerators/NetEscapades.EnumGenerators.csproj b/src/NetEscapades.EnumGenerators/NetEscapades.EnumGenerators.csproj index 363d543..7a51ae5 100644 --- a/src/NetEscapades.EnumGenerators/NetEscapades.EnumGenerators.csproj +++ b/src/NetEscapades.EnumGenerators/NetEscapades.EnumGenerators.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs b/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs index 2784922..4ac4e4e 100644 --- a/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs +++ b/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs @@ -61,12 +61,20 @@ namespace ").Append(enumToGenerate.Namespace).Append(@" public static string ToStringFast(this ").Append(enumToGenerate.FullyQualifiedName).Append(@" value) => value switch {"); - foreach (var member in enumToGenerate.Values) + foreach (var member in enumToGenerate.Names) { sb.Append(@" ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key) - .Append(" => nameof(") - .Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append("),"); + .Append(" => "); + + if (member.Value.DisplayName is null) + { + sb.Append("nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append("),"); + } + else + { + sb.Append('"').Append(member.Value.DisplayName).Append(@""","); + } } sb.Append(@" @@ -90,7 +98,7 @@ public static bool HasFlag(this ").Append(enumToGenerate.FullyQualifiedName).App public static bool IsDefined(").Append(enumToGenerate.FullyQualifiedName).Append(@" value) => value switch {"); - foreach (var member in enumToGenerate.Values) + foreach (var member in enumToGenerate.Names) { sb.Append(@" ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key) @@ -98,102 +106,203 @@ public static bool IsDefined(").Append(enumToGenerate.FullyQualifiedName).Append } sb.Append(@" _ => false, - }; + };"); - public static bool IsDefined(string name) - => name switch - {"); - foreach (var member in enumToGenerate.Values) + sb.Append(@" + + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + {"); + if (enumToGenerate.IsDisplaAttributeUsed) { sb.Append(@" + var isDefinedInDisplayAttribute = false; + if (allowMatchingMetadataAttribute) + { + isDefinedInDisplayAttribute = name switch + {"); + foreach (var member in enumToGenerate.Names) + { + if (member.Value.DisplayName is not null && member.Value.IsDisplayNameTheFirstPresence) + { + sb.Append(@" + """).Append(member.Value.DisplayName).Append(@""" => true,"); + } + } + + sb.Append(@" + _ => false, + }; + } + + if (isDefinedInDisplayAttribute) + { + return true; + } + + "); + } + sb.Append(@" + return name switch + {"); + foreach (var member in enumToGenerate.Names) + { + sb.Append(@" nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@") => true,"); } sb.Append(@" _ => false, }; + }"); + + sb.Append(@" public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out ").Append(enumToGenerate.FullyQualifiedName).Append(@" value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false);"); + sb.Append(@" - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out ").Append(enumToGenerate.FullyQualifiedName).Append(@" value) - { - switch (name) - {"); - foreach (var member in enumToGenerate.Values) - { - sb.Append(@" - case { } s when s.Equals(nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"), System.StringComparison.OrdinalIgnoreCase): - value = ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"; - return true;"); - } - + out ").Append(enumToGenerate.FullyQualifiedName).Append(@" value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false);"); sb.Append(@" - case { } s when ").Append(enumToGenerate.UnderlyingType).Append(@".TryParse(name, out var val): - value = (").Append(enumToGenerate.FullyQualifiedName).Append(@")val; - return true; - default: - value = default; - return false; - } - } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out ").Append(enumToGenerate.FullyQualifiedName).Append(@" value) + out ").Append(enumToGenerate.FullyQualifiedName).Append(@" value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + {"); + + if (enumToGenerate.IsDisplaAttributeUsed) { - switch (name) - {"); - foreach (var member in enumToGenerate.Values) + sb.Append(@" + if (allowMatchingMetadataAttribute) + { + if (ignoreCase) + { + switch (name) + {"); + foreach (var member in enumToGenerate.Names) + { + if (member.Value.DisplayName is not null && member.Value.IsDisplayNameTheFirstPresence) + { + sb.Append(@" + case string s when s.Equals(""").Append(member.Value.DisplayName).Append(@""", System.StringComparison.OrdinalIgnoreCase): + value = ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"; + return true;"); + } + } + + sb.Append(@" + default: + break; + }; + } + else + { + switch (name) + {"); + foreach (var member in enumToGenerate.Names) + { + if (member.Value.DisplayName is not null && member.Value.IsDisplayNameTheFirstPresence) + { + sb.Append(@" + case """).Append(member.Value.DisplayName).Append(@""": + value = ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"; + return true;"); + } + } + + sb.Append(@" + default: + break; + }; + } + }"); + } + + sb.Append(@" + + if (ignoreCase) + { + switch (name) + {"); + foreach (var member in enumToGenerate.Names) { sb.Append(@" - case nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"): - value = ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"; - return true;"); + case string s when s.Equals(nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"), System.StringComparison.OrdinalIgnoreCase): + value = ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"; + return true;"); } sb.Append(@" - case { } s when ").Append(enumToGenerate.UnderlyingType).Append(@".TryParse(name, out var val): - value = (").Append(enumToGenerate.FullyQualifiedName).Append(@")val; - return true; - default: - value = default; - return false; + case string s when ").Append(enumToGenerate.UnderlyingType).Append(@".TryParse(name, out var val): + value = (").Append(enumToGenerate.FullyQualifiedName).Append(@")val; + return true; + default: + value = default; + return false; + } } + else + { + switch (name) + {"); + foreach (var member in enumToGenerate.Names) + { + sb.Append(@" + case nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"): + value = ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"; + return true;"); } + sb.Append(@" + case string s when ").Append(enumToGenerate.UnderlyingType).Append(@".TryParse(name, out var val): + value = (").Append(enumToGenerate.FullyQualifiedName).Append(@")val; + return true; + default: + value = default; + return false; + } + } + }"); + + sb.Append(@" + public static ").Append(enumToGenerate.FullyQualifiedName).Append(@"[] GetValues() { return new[] {"); - foreach (var member in enumToGenerate.Values) + foreach (var member in enumToGenerate.Names) { sb.Append(@" ").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(','); } + sb.Append(@" }; - } + }"); + + sb.Append(@" public static string[] GetNames() { return new[] {"); - foreach (var member in enumToGenerate.Values) + foreach (var member in enumToGenerate.Names) { sb.Append(@" nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append("),"); diff --git a/tests/NetEscapades.EnumGenerators.Benchmarks/EnumHelper.cs b/tests/NetEscapades.EnumGenerators.Benchmarks/EnumHelper.cs new file mode 100644 index 0000000..3e268dd --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Benchmarks/EnumHelper.cs @@ -0,0 +1,77 @@ +using System.ComponentModel.DataAnnotations; +using System.Reflection; + +namespace NetEscapades.EnumGenerators.Benchmarks; + +internal static class EnumHelper where T : struct +{ + internal static bool TryParseByDisplayName(string name, bool ignoreCase, out T enumValue) + { + enumValue = default; + + var stringComparisonOption = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + var enumValues = (T[])Enum.GetValues(typeof(T)); + foreach (var value in enumValues) + { + if (TryGetDisplayName(value.ToString(), out var displayName) && displayName.Equals(name, stringComparisonOption)) + { + enumValue = value; + return true; + } + } + + return false; + } + + private static bool TryGetDisplayName( + string? value, +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? displayName) +#else + out string? displayName) +#endif + { + displayName = default; + + if (typeof(T).IsEnum) + { + // Prevent: Warning CS8604 Possible null reference argument for parameter 'name' in 'MemberInfo[] Type.GetMember(string name)' + if (value is not null) + { + var memberInfo = typeof(T).GetMember(value); + if (memberInfo.Length > 0) + { + displayName = memberInfo[0].GetCustomAttribute()?.GetName(); + if (displayName is null) + { + return false; + } + + return true; + } + } + } + + return false; + } + + internal static string GetDisplayName(T value) + { + if (typeof(T).IsEnum) + { + var memberInfo = typeof(T).GetMember(value.ToString()); + if (memberInfo.Length > 0) + { + var displayName = memberInfo[0].GetCustomAttribute().GetName(); + if (displayName is null) + { + return string.Empty; + } + + return displayName; + } + } + + return string.Empty; + } +} diff --git a/tests/NetEscapades.EnumGenerators.Benchmarks/NetEscapades.EnumGenerators.Benchmarks.csproj b/tests/NetEscapades.EnumGenerators.Benchmarks/NetEscapades.EnumGenerators.Benchmarks.csproj index a0a9b6d..51dbbdd 100644 --- a/tests/NetEscapades.EnumGenerators.Benchmarks/NetEscapades.EnumGenerators.Benchmarks.csproj +++ b/tests/NetEscapades.EnumGenerators.Benchmarks/NetEscapades.EnumGenerators.Benchmarks.csproj @@ -11,13 +11,12 @@ + - - + + diff --git a/tests/NetEscapades.EnumGenerators.Benchmarks/Program.cs b/tests/NetEscapades.EnumGenerators.Benchmarks/Program.cs index 7be332e..dcd5e62 100644 --- a/tests/NetEscapades.EnumGenerators.Benchmarks/Program.cs +++ b/tests/NetEscapades.EnumGenerators.Benchmarks/Program.cs @@ -1,7 +1,9 @@ -using System.Runtime.CompilerServices; +using System.ComponentModel.DataAnnotations; +using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using NetEscapades.EnumGenerators; +using NetEscapades.EnumGenerators.Benchmarks; BenchmarkSwitcher .FromAssembly(typeof(Program).Assembly) @@ -11,6 +13,8 @@ public enum TestEnum { First = 0, + + [Display(Name = "2nd")] Second = 1, Third = 2, } @@ -27,6 +31,13 @@ public string EnumToString() return _enum.ToString(); } + [Benchmark] + [MethodImpl(MethodImplOptions.NoInlining)] + public string EnumToStringDisplayNameWithReflection() + { + return EnumHelper.GetDisplayName(_enum); + } + [Benchmark] [MethodImpl(MethodImplOptions.NoInlining)] public string ToStringFast() @@ -59,6 +70,7 @@ public bool ExtensionsIsDefinedName() public class IsDefinedNameBenchmark { private static readonly string _enum = nameof(TestEnum.Second); + private static readonly string _enumDisplaName = "2nd"; [Benchmark(Baseline = true)] [MethodImpl(MethodImplOptions.NoInlining)] @@ -71,7 +83,21 @@ public bool EnumIsDefined() [MethodImpl(MethodImplOptions.NoInlining)] public bool ExtensionsIsDefined() { - return TestEnumExtensions.IsDefined(_enum); + return TestEnumExtensions.IsDefined(_enum, allowMatchingMetadataAttribute: false); + } + + [Benchmark] + [MethodImpl(MethodImplOptions.NoInlining)] + public bool EnumIsDefinedNameDisplayNameWithReflection() + { + return EnumHelper.TryParseByDisplayName(_enumDisplaName, ignoreCase: false, out _); + } + + [Benchmark] + [MethodImpl(MethodImplOptions.NoInlining)] + public bool ExtensionsIsDefinedNameDisplayName() + { + return TestEnumExtensions.IsDefined(_enumDisplaName, allowMatchingMetadataAttribute: true); } } @@ -145,7 +171,23 @@ public TestEnum EnumTryParse() [MethodImpl(MethodImplOptions.NoInlining)] public TestEnum ExtensionsTryParse() { - return TestEnumExtensions.TryParse("Second", ignoreCase: false, out TestEnum result) + return TestEnumExtensions.TryParse("Second", out TestEnum result, ignoreCase: false) + ? result + : default; + } + + [Benchmark] + [MethodImpl(MethodImplOptions.NoInlining)] + public TestEnum EnumTryParseDisplayNameWithReflection() + { + return EnumHelper.TryParseByDisplayName("2nd", ignoreCase: false, out TestEnum result) ? result : default; + } + + [Benchmark] + [MethodImpl(MethodImplOptions.NoInlining)] + public TestEnum ExtensionsTryParseDisplayName() + { + return TestEnumExtensions.TryParse("2nd", out TestEnum result, ignoreCase: false, allowMatchingMetadataAttribute: true) ? result : default; } @@ -167,7 +209,23 @@ public TestEnum EnumTryParseIgnoreCase() [MethodImpl(MethodImplOptions.NoInlining)] public TestEnum ExtensionsTryParseIgnoreCase() { - return TestEnumExtensions.TryParse("second", ignoreCase: true, out TestEnum result) + return TestEnumExtensions.TryParse("second", out TestEnum result, ignoreCase: true) + ? result + : default; + } + + [Benchmark] + [MethodImpl(MethodImplOptions.NoInlining)] + public TestEnum EnumTryParseIgnoreCaseDisplayNameWithReflection() + { + return EnumHelper.TryParseByDisplayName("2ND", ignoreCase: true, out TestEnum result) ? result : default; + } + + [Benchmark] + [MethodImpl(MethodImplOptions.NoInlining)] + public TestEnum ExtensionsTryParseIgnoreCaseDisplayName() + { + return TestEnumExtensions.TryParse("2ND", out TestEnum result, ignoreCase: true, allowMatchingMetadataAttribute: true) ? result : default; } diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumInNamespaceExtensionsTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumInNamespaceExtensionsTests.cs index 230fe96..c67782b 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumInNamespaceExtensionsTests.cs +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumInNamespaceExtensionsTests.cs @@ -15,6 +15,8 @@ public class EnumInNamespaceExtensionsTests : ExtensionTests { "First", "Second", + "2nd", + "2ND", "first", "SECOND", "3", @@ -28,9 +30,9 @@ public class EnumInNamespaceExtensionsTests : ExtensionTests protected override string ToStringFast(EnumInNamespace value) => value.ToStringFast(); protected override bool IsDefined(EnumInNamespace value) => EnumInNamespaceExtensions.IsDefined(value); - protected override bool IsDefined(string name) => EnumInNamespaceExtensions.IsDefined(name); - protected override bool TryParse(string name,bool ignoreCase, out EnumInNamespace parsed) - => EnumInNamespaceExtensions.TryParse(name, ignoreCase, out parsed); + protected override bool IsDefined(string name, bool allowMatchingMetadataAttribute) => EnumInNamespaceExtensions.IsDefined(name, allowMatchingMetadataAttribute: false); + protected override bool TryParse(string name,bool ignoreCase, out EnumInNamespace parsed, bool allowMatchingMetadataAttribute) + => EnumInNamespaceExtensions.TryParse(name, out parsed, ignoreCase); [Theory] [MemberData(nameof(ValidEnumValues))] @@ -42,15 +44,15 @@ protected override bool TryParse(string name,bool ignoreCase, out EnumInNamespac [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name); + public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name, allowMatchingMetadataAttribute: false); [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesTryParse(string name) => GeneratesTryParseTest(name); + public void GeneratesTryParse(string name) => GeneratesTryParseTest(name, ignoreCase: false, allowMatchingMetadataAttribute: false); [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseIgnoreCaseTest(name); + public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseTest(name, ignoreCase: true, allowMatchingMetadataAttribute: false); [Fact] public void GeneratesGetValues() => GeneratesGetValuesTest(EnumInNamespaceExtensions.GetValues()); diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumWithDisplayNameInNamespaceExtensionsTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumWithDisplayNameInNamespaceExtensionsTests.cs new file mode 100644 index 0000000..3e7704c --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumWithDisplayNameInNamespaceExtensionsTests.cs @@ -0,0 +1,74 @@ +using Xunit; + +namespace NetEscapades.EnumGenerators.IntegrationTests; + +public class EnumWithDisplayNameInNamespaceExtensionsTests : ExtensionTests +{ + public static TheoryData ValidEnumValues() => new() + { + EnumWithDisplayNameInNamespace.First, + EnumWithDisplayNameInNamespace.Second, + (EnumWithDisplayNameInNamespace)3, + }; + + public static TheoryData ValuesToParse() => new() + { + "First", + "Second", + "2nd", + "2ND", + "first", + "SECOND", + "3", + "267", + "-267", + "2147483647", + "3000000000", + "Fourth", + "Fifth", + }; + + protected override string ToStringFast(EnumWithDisplayNameInNamespace value) => value.ToStringFast(); + protected override bool IsDefined(EnumWithDisplayNameInNamespace value) => EnumWithDisplayNameInNamespaceExtensions.IsDefined(value); + protected override bool IsDefined(string name, bool allowMatchingMetadataAttribute) => EnumWithDisplayNameInNamespaceExtensions.IsDefined(name, allowMatchingMetadataAttribute); + protected override bool TryParse(string name,bool ignoreCase, out EnumWithDisplayNameInNamespace parsed, bool allowMatchingMetadataAttribute) + => EnumWithDisplayNameInNamespaceExtensions.TryParse(name, out parsed, ignoreCase, allowMatchingMetadataAttribute); + + [Theory] + [MemberData(nameof(ValidEnumValues))] + public void GeneratesToStringFast(EnumWithDisplayNameInNamespace value) => GeneratesToStringFastTest(value); + + [Theory] + [MemberData(nameof(ValidEnumValues))] + public void GeneratesIsDefined(EnumWithDisplayNameInNamespace value) => GeneratesIsDefinedTest(value); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name, allowMatchingMetadataAttribute: false); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesIsDefinedUsingNameallowMatchingMetadataAttribute(string name) => GeneratesIsDefinedTest(name, allowMatchingMetadataAttribute: true); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParse(string name) => GeneratesTryParseTest(name, ignoreCase: false, allowMatchingMetadataAttribute: false); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParseallowMatchingMetadataAttribute(string name) => GeneratesTryParseTest(name, ignoreCase: false, allowMatchingMetadataAttribute: true); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseTest(name, ignoreCase: true, allowMatchingMetadataAttribute: false); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParseIgnoreCaseallowMatchingMetadataAttribute(string name) => GeneratesTryParseTest(name, ignoreCase: true, allowMatchingMetadataAttribute: true); + + [Fact] + public void GeneratesGetValues() => GeneratesGetValuesTest(EnumWithDisplayNameInNamespaceExtensions.GetValues()); + + [Fact] + public void GeneratesGetNames() => base.GeneratesGetNamesTest(EnumWithDisplayNameInNamespaceExtensions.GetNames()); +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumWithSameDisplayNameExtensionsTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumWithSameDisplayNameExtensionsTests.cs new file mode 100644 index 0000000..3b0e824 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/EnumWithSameDisplayNameExtensionsTests.cs @@ -0,0 +1,74 @@ +using Xunit; + +namespace NetEscapades.EnumGenerators.IntegrationTests; + +public class EnumWithSameDisplayNameExtensionsTests : ExtensionTests +{ + public static TheoryData ValidEnumValues() => new() + { + EnumWithSameDisplayName.First, + EnumWithSameDisplayName.Second, + (EnumWithSameDisplayName)3, + }; + + public static TheoryData ValuesToParse() => new() + { + "First", + "Second", + "2nd", + "2ND", + "first", + "SECOND", + "3", + "267", + "-267", + "2147483647", + "3000000000", + "Fourth", + "Fifth", + }; + + protected override string ToStringFast(EnumWithSameDisplayName value) => value.ToStringFast(); + protected override bool IsDefined(EnumWithSameDisplayName value) => EnumWithSameDisplayNameExtensions.IsDefined(value); + protected override bool IsDefined(string name, bool allowMatchingMetadataAttribute) => EnumWithSameDisplayNameExtensions.IsDefined(name, allowMatchingMetadataAttribute); + protected override bool TryParse(string name,bool ignoreCase, out EnumWithSameDisplayName parsed, bool allowMatchingMetadataAttribute) + => EnumWithSameDisplayNameExtensions.TryParse(name, out parsed, ignoreCase, allowMatchingMetadataAttribute); + + [Theory] + [MemberData(nameof(ValidEnumValues))] + public void GeneratesToStringFast(EnumWithSameDisplayName value) => GeneratesToStringFastTest(value); + + [Theory] + [MemberData(nameof(ValidEnumValues))] + public void GeneratesIsDefined(EnumWithSameDisplayName value) => GeneratesIsDefinedTest(value); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name, allowMatchingMetadataAttribute: false); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesIsDefinedUsingNameallowMatchingMetadataAttribute(string name) => GeneratesIsDefinedTest(name, allowMatchingMetadataAttribute: true); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParse(string name) => GeneratesTryParseTest(name, ignoreCase: false, allowMatchingMetadataAttribute: false); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParseallowMatchingMetadataAttribute(string name) => GeneratesTryParseTest(name, ignoreCase: false, allowMatchingMetadataAttribute: true); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseTest(name, ignoreCase: true, allowMatchingMetadataAttribute: false); + + [Theory] + [MemberData(nameof(ValuesToParse))] + public void GeneratesTryParseIgnoreCaseallowMatchingMetadataAttribute(string name) => GeneratesTryParseTest(name, ignoreCase: true, allowMatchingMetadataAttribute: true); + + [Fact] + public void GeneratesGetValues() => GeneratesGetValuesTest(EnumWithSameDisplayNameExtensions.GetValues()); + + [Fact] + public void GeneratesGetNames() => base.GeneratesGetNamesTest(EnumWithSameDisplayNameExtensions.GetNames()); +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs index 63be69d..1e382fb 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; namespace NetEscapades.EnumGenerators.IntegrationTests { @@ -9,6 +10,29 @@ public enum EnumInNamespace Second = 1, Third = 2, } + + [EnumExtensions] + public enum EnumWithDisplayNameInNamespace + { + First = 0, + + [Display(Name = "2nd")] + Second = 1, + + Third = 2, + } + + [EnumExtensions] + public enum EnumWithSameDisplayName + { + First = 0, + + [Display(Name = "2nd")] + Second = 1, + + [Display(Name = "2nd")] + Third = 2, + } [EnumExtensions] public enum LongEnum: long diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/ExtensionTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/ExtensionTests.cs index bd800f5..c1cf6d0 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/ExtensionTests.cs +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/ExtensionTests.cs @@ -1,21 +1,28 @@ using System; +using System.ComponentModel.DataAnnotations; +using System.Reflection; using FluentAssertions; using FluentAssertions.Execution; namespace NetEscapades.EnumGenerators.IntegrationTests; +#nullable enable public abstract class ExtensionTests where T : struct { protected abstract string ToStringFast(T value); protected abstract bool IsDefined(T value); - protected abstract bool IsDefined(string name); - protected abstract bool TryParse(string name, bool ignoreCase, out T parsed); + protected abstract bool IsDefined(string name, bool allowMatchingMetadataAttribute = false); + protected abstract bool TryParse(string name, bool ignoreCase, out T parsed, bool allowMatchingMetadataAttribute = false); protected void GeneratesToStringFastTest(T value) { var serialized = ToStringFast(value); + var valueAsString = value.ToString(); - serialized.Should().Be(value.ToString()); + TryGetDisplayName(valueAsString, out var displayName); + var expectedValue = displayName is null ? valueAsString : displayName; + + serialized.Should().Be(expectedValue); } protected void GeneratesIsDefinedTest(T value) @@ -25,29 +32,48 @@ protected void GeneratesIsDefinedTest(T value) isDefined.Should().Be(Enum.IsDefined(typeof(T), value)); } - protected void GeneratesIsDefinedTest(string name) + protected void GeneratesIsDefinedTest(string name, bool allowMatchingMetadataAttribute) { - var isDefined = IsDefined(name); + bool expectedResult; + var isDefined = IsDefined(name, allowMatchingMetadataAttribute); - isDefined.Should().Be(Enum.IsDefined(typeof(T), name)); - } + if (allowMatchingMetadataAttribute) + { + expectedResult = TryGetEnumByDisplayName(name, ignoreCase: false, out _); + if (!expectedResult) + { + expectedResult = Enum.IsDefined(typeof(T), name); + } + } + else + { + expectedResult = Enum.IsDefined(typeof(T), name); + } - protected void GeneratesTryParseTest(string name) - { - var isValid = Enum.TryParse(name, out T expected); - var result = TryParse(name, ignoreCase: false, out var parsed); - using var _ = new AssertionScope(); - result.Should().Be(isValid); - parsed.Should().Be(expected); + isDefined.Should().Be(expectedResult); } - protected void GeneratesTryParseIgnoreCaseTest(string name) + protected void GeneratesTryParseTest(string name, bool ignoreCase, bool allowMatchingMetadataAttribute) { - var isValid = Enum.TryParse(name, ignoreCase: true, out T expected); - var result = TryParse(name, ignoreCase: true, out var parsed); - using var _ = new AssertionScope(); - result.Should().Be(isValid); - parsed.Should().Be(expected); + bool expectedValidity; + T expectedResult; + var isValid = TryParse(name, ignoreCase, out var result, allowMatchingMetadataAttribute); + + if (allowMatchingMetadataAttribute) + { + expectedValidity = TryGetEnumByDisplayName(name, ignoreCase, out expectedResult); + if (!expectedValidity) + { + expectedValidity = Enum.TryParse(name, ignoreCase, out expectedResult); + } + } + else + { + expectedValidity = Enum.TryParse(name, ignoreCase, out expectedResult); + } + _ = new AssertionScope(); + isValid.Should().Be(expectedValidity); + result.Should().Be(expectedResult); } protected void GeneratesGetValuesTest(T[] values) @@ -61,4 +87,54 @@ protected void GeneratesGetNamesTest(string[] names) var expected = Enum.GetNames(typeof(T)); names.Should().Equal(expected); } + + private bool TryGetEnumByDisplayName(string name, bool ignoreCase, out T enumValue) + { + enumValue = default; + + var stringComparisonOptions = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + var enumValues = (T[])Enum.GetValues(typeof(T)); + foreach (var value in enumValues) + { + if (TryGetDisplayName(value.ToString(), out var displayName) && displayName.Equals(name, stringComparisonOptions)) + { + enumValue = value; + return true; + } + } + + return false; + } + + private bool TryGetDisplayName( + string? value, +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? displayName) +#else + out string? displayName) +#endif + { + displayName = default; + + if (typeof(T).IsEnum) + { + // Prevent: Warning CS8604 Possible null reference argument for parameter 'name' in 'MemberInfo[] Type.GetMember(string name)' + if (value is not null) + { + var memberInfo = typeof(T).GetMember(value); + if (memberInfo.Length > 0) + { + displayName = memberInfo[0].GetCustomAttribute()?.GetName(); + if (displayName is null) + { + return false; + } + + return true; + } + } + } + + return false; + } } \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs index ecee7b0..b4fe650 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/FlagsEnumExtensionsTests.cs @@ -1,4 +1,3 @@ -using System; using FluentAssertions; using Xunit; @@ -18,6 +17,8 @@ public class FlagsEnumExtensionsTests : ExtensionTests { "First", "Second", + "2nd", + "2ND", "first", "SECOND", "3", @@ -31,9 +32,9 @@ public class FlagsEnumExtensionsTests : ExtensionTests protected override string ToStringFast(FlagsEnum value) => value.ToStringFast(); protected override bool IsDefined(FlagsEnum value) => FlagsEnumExtensions.IsDefined(value); - protected override bool IsDefined(string name) => FlagsEnumExtensions.IsDefined(name); - protected override bool TryParse(string name,bool ignoreCase, out FlagsEnum parsed) - => FlagsEnumExtensions.TryParse(name, ignoreCase, out parsed); + protected override bool IsDefined(string name, bool allowMatchingMetadataAttribute) => FlagsEnumExtensions.IsDefined(name, allowMatchingMetadataAttribute: false); + protected override bool TryParse(string name,bool ignoreCase, out FlagsEnum parsed, bool allowMatchingMetadataAttribute) + => FlagsEnumExtensions.TryParse(name, out parsed, ignoreCase); [Theory] [MemberData(nameof(ValidEnumValues))] @@ -45,7 +46,7 @@ protected override bool TryParse(string name,bool ignoreCase, out FlagsEnum pars [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name); + public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name, allowMatchingMetadataAttribute: false); [Theory] [InlineData(FlagsEnum.First)] @@ -63,11 +64,11 @@ public void HasFlags(FlagsEnum value) [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesTryParse(string name) => GeneratesTryParseTest(name); + public void GeneratesTryParse(string name) => GeneratesTryParseTest(name, ignoreCase: false, allowMatchingMetadataAttribute: false); [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseIgnoreCaseTest(name); + public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseTest(name, ignoreCase: true, allowMatchingMetadataAttribute: false); [Fact] public void GeneratesGetValues() => GeneratesGetValuesTest(FlagsEnumExtensions.GetValues()); diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/LongEnumExtensionsTests.cs b/tests/NetEscapades.EnumGenerators.IntegrationTests/LongEnumExtensionsTests.cs index 744352d..db50b1b 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/LongEnumExtensionsTests.cs +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/LongEnumExtensionsTests.cs @@ -15,6 +15,8 @@ public class LongEnumExtensionsTests : ExtensionTests { "First", "Second", + "2nd", + "2ND", "first", "SECOND", "3", @@ -28,9 +30,9 @@ public class LongEnumExtensionsTests : ExtensionTests protected override string ToStringFast(LongEnum value) => value.ToStringFast(); protected override bool IsDefined(LongEnum value) => LongEnumExtensions.IsDefined(value); - protected override bool IsDefined(string name) => LongEnumExtensions.IsDefined(name); - protected override bool TryParse(string name,bool ignoreCase, out LongEnum parsed) - => LongEnumExtensions.TryParse(name, ignoreCase, out parsed); + protected override bool IsDefined(string name, bool allowMatchingMetadataAttribute) => LongEnumExtensions.IsDefined(name, allowMatchingMetadataAttribute: false); + protected override bool TryParse(string name,bool ignoreCase, out LongEnum parsed, bool allowMatchingMetadataAttribute) + => LongEnumExtensions.TryParse(name, out parsed, ignoreCase); [Theory] [MemberData(nameof(ValidEnumValues))] @@ -42,15 +44,15 @@ protected override bool TryParse(string name,bool ignoreCase, out LongEnum parse [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name); + public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name, allowMatchingMetadataAttribute: false); [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesTryParse(string name) => GeneratesTryParseTest(name); + public void GeneratesTryParse(string name) => GeneratesTryParseTest(name, ignoreCase:false, allowMatchingMetadataAttribute: false); [Theory] [MemberData(nameof(ValuesToParse))] - public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseIgnoreCaseTest(name); + public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseTest(name, ignoreCase: true, allowMatchingMetadataAttribute: false); [Fact] public void GeneratesGetValues() => GeneratesGetValuesTest(LongEnumExtensions.GetValues()); diff --git a/tests/NetEscapades.EnumGenerators.IntegrationTests/NetEscapades.EnumGenerators.IntegrationTests.csproj b/tests/NetEscapades.EnumGenerators.IntegrationTests/NetEscapades.EnumGenerators.IntegrationTests.csproj index bdda297..e296e5b 100644 --- a/tests/NetEscapades.EnumGenerators.IntegrationTests/NetEscapades.EnumGenerators.IntegrationTests.csproj +++ b/tests/NetEscapades.EnumGenerators.IntegrationTests/NetEscapades.EnumGenerators.IntegrationTests.csproj @@ -7,10 +7,8 @@ - - + + @@ -21,6 +19,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests.csproj b/tests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests.csproj index 847f1c7..0282e22 100644 --- a/tests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests.csproj +++ b/tests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests/NetEscapades.EnumGenerators.Nuget.Attributes.IntegrationTests.csproj @@ -9,21 +9,22 @@ - + - + - + - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/NetEscapades.EnumGenerators.Nuget.IntegrationTests/NetEscapades.EnumGenerators.Nuget.IntegrationTests.csproj b/tests/NetEscapades.EnumGenerators.Nuget.IntegrationTests/NetEscapades.EnumGenerators.Nuget.IntegrationTests.csproj index c03fff5..bed8257 100644 --- a/tests/NetEscapades.EnumGenerators.Nuget.IntegrationTests/NetEscapades.EnumGenerators.Nuget.IntegrationTests.csproj +++ b/tests/NetEscapades.EnumGenerators.Nuget.IntegrationTests/NetEscapades.EnumGenerators.Nuget.IntegrationTests.csproj @@ -8,21 +8,22 @@ - + - + - + - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/NetEscapades.EnumGenerators.Tests/EnumGeneratorTests.cs b/tests/NetEscapades.EnumGenerators.Tests/EnumGeneratorTests.cs index 68d914e..149cc86 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/EnumGeneratorTests.cs +++ b/tests/NetEscapades.EnumGenerators.Tests/EnumGeneratorTests.cs @@ -134,4 +134,58 @@ internal enum MyEnum Assert.Empty(diagnostics); return Verifier.Verify(output).UseDirectory("Snapshots"); } + + [Fact] + public Task CanGenerateEnumExtensionsWithDisplayName() + { + const string input = @"using NetEscapades.EnumGenerators; +using System.ComponentModel.DataAnnotations; + +namespace MyTestNameSpace +{ + [EnumExtensions] + public enum MyEnum + { + First = 0, + + [Display(Name = ""2nd"")] + Second = 1, + Third = 2, + + [Display(Name = ""4th"")] + Fourth = 3 + } +}"; + var (diagnostics, output) = TestHelpers.GetGeneratedOutput(input); + + Assert.Empty(diagnostics); + return Verifier.Verify(output).UseDirectory("Snapshots"); + } + + [Fact] + public Task CanGenerateEnumExtensionsWithSameDisplayName() + { + const string input = @"using NetEscapades.EnumGenerators; +using System.ComponentModel.DataAnnotations; + +namespace MyTestNameSpace +{ + [EnumExtensions] + public enum MyEnum + { + First = 0, + + [Display(Name = ""2nd"")] + Second = 1, + Third = 2, + + [Display(Name = ""2nd"")] + Fourth = 3 + } +}"; + var (diagnostics, output) = TestHelpers.GetGeneratedOutput(input); + + Assert.Empty(diagnostics); + return Verifier.Verify(output).UseDirectory("Snapshots"); + } } \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/NetEscapades.EnumGenerators.Tests.csproj b/tests/NetEscapades.EnumGenerators.Tests/NetEscapades.EnumGenerators.Tests.csproj index 59c36ac..e2fd0fc 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/NetEscapades.EnumGenerators.Tests.csproj +++ b/tests/NetEscapades.EnumGenerators.Tests/NetEscapades.EnumGenerators.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInChildNamespace.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInChildNamespace.verified.txt index 38bd155..66a7cd4 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInChildNamespace.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInChildNamespace.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -28,68 +28,78 @@ namespace MyTestNameSpace _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(MyTestNameSpace.MyEnum.First) => true, nameof(MyTestNameSpace.MyEnum.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out MyTestNameSpace.MyEnum value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) - { - switch (name) - { - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.First; - return true; - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; - } - } + out MyTestNameSpace.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) + out MyTestNameSpace.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.First; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(MyTestNameSpace.MyEnum.First): - value = MyTestNameSpace.MyEnum.First; - return true; - case nameof(MyTestNameSpace.MyEnum.Second): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(MyTestNameSpace.MyEnum.First): + value = MyTestNameSpace.MyEnum.First; + return true; + case nameof(MyTestNameSpace.MyEnum.Second): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInGlobalNamespace.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInGlobalNamespace.verified.txt index f2d20b5..159d555 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInGlobalNamespace.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInGlobalNamespace.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -26,68 +26,78 @@ _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(MyEnum.First) => true, nameof(MyEnum.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out MyEnum value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyEnum value) - { - switch (name) - { - case { } s when s.Equals(nameof(MyEnum.First), System.StringComparison.OrdinalIgnoreCase): - value = MyEnum.First; - return true; - case { } s when s.Equals(nameof(MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): - value = MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyEnum)val; - return true; - default: - value = default; - return false; - } - } + out MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyEnum value) + out MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyEnum.First; + return true; + case string s when s.Equals(nameof(MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(MyEnum.First): - value = MyEnum.First; - return true; - case nameof(MyEnum.Second): - value = MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyEnum)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(MyEnum.First): + value = MyEnum.First; + return true; + case nameof(MyEnum.Second): + value = MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyEnum)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInNestedClass.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInNestedClass.verified.txt index d16bc50..03f482d 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInNestedClass.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsInNestedClass.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -28,68 +28,78 @@ namespace MyTestNameSpace _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(MyTestNameSpace.InnerClass.MyEnum.First) => true, nameof(MyTestNameSpace.InnerClass.MyEnum.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out MyTestNameSpace.InnerClass.MyEnum value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.InnerClass.MyEnum value) - { - switch (name) - { - case { } s when s.Equals(nameof(MyTestNameSpace.InnerClass.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.InnerClass.MyEnum.First; - return true; - case { } s when s.Equals(nameof(MyTestNameSpace.InnerClass.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.InnerClass.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.InnerClass.MyEnum)val; - return true; - default: - value = default; - return false; - } - } + out MyTestNameSpace.InnerClass.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.InnerClass.MyEnum value) + out MyTestNameSpace.InnerClass.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyTestNameSpace.InnerClass.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.InnerClass.MyEnum.First; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.InnerClass.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.InnerClass.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.InnerClass.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(MyTestNameSpace.InnerClass.MyEnum.First): - value = MyTestNameSpace.InnerClass.MyEnum.First; - return true; - case nameof(MyTestNameSpace.InnerClass.MyEnum.Second): - value = MyTestNameSpace.InnerClass.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.InnerClass.MyEnum)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(MyTestNameSpace.InnerClass.MyEnum.First): + value = MyTestNameSpace.InnerClass.MyEnum.First; + return true; + case nameof(MyTestNameSpace.InnerClass.MyEnum.Second): + value = MyTestNameSpace.InnerClass.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.InnerClass.MyEnum)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomName.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomName.verified.txt index 72d3489..659a7db 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomName.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomName.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -28,68 +28,78 @@ namespace MyTestNameSpace _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(MyTestNameSpace.MyEnum.First) => true, nameof(MyTestNameSpace.MyEnum.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out MyTestNameSpace.MyEnum value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) - { - switch (name) - { - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.First; - return true; - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; - } - } + out MyTestNameSpace.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) + out MyTestNameSpace.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.First; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(MyTestNameSpace.MyEnum.First): - value = MyTestNameSpace.MyEnum.First; - return true; - case nameof(MyTestNameSpace.MyEnum.Second): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(MyTestNameSpace.MyEnum.First): + value = MyTestNameSpace.MyEnum.First; + return true; + case nameof(MyTestNameSpace.MyEnum.Second): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespace.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespace.verified.txt index 65945a1..8a6469e 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespace.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespace.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -28,68 +28,78 @@ namespace A.B _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(MyTestNameSpace.MyEnum.First) => true, nameof(MyTestNameSpace.MyEnum.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out MyTestNameSpace.MyEnum value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) - { - switch (name) - { - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.First; - return true; - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; - } - } + out MyTestNameSpace.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) + out MyTestNameSpace.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.First; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(MyTestNameSpace.MyEnum.First): - value = MyTestNameSpace.MyEnum.First; - return true; - case nameof(MyTestNameSpace.MyEnum.Second): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(MyTestNameSpace.MyEnum.First): + value = MyTestNameSpace.MyEnum.First; + return true; + case nameof(MyTestNameSpace.MyEnum.Second): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespaceAndName.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespaceAndName.verified.txt index 4519c90..defa488 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespaceAndName.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithCustomNamespaceAndName.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -28,68 +28,78 @@ namespace A.B _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(MyTestNameSpace.MyEnum.First) => true, nameof(MyTestNameSpace.MyEnum.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out MyTestNameSpace.MyEnum value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) - { - switch (name) - { - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.First; - return true; - case { } s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; - } - } + out MyTestNameSpace.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out MyTestNameSpace.MyEnum value) + out MyTestNameSpace.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.First; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(MyTestNameSpace.MyEnum.First): - value = MyTestNameSpace.MyEnum.First; - return true; - case nameof(MyTestNameSpace.MyEnum.Second): - value = MyTestNameSpace.MyEnum.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (MyTestNameSpace.MyEnum)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(MyTestNameSpace.MyEnum.First): + value = MyTestNameSpace.MyEnum.First; + return true; + case nameof(MyTestNameSpace.MyEnum.Second): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithDisplayName.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithDisplayName.verified.txt new file mode 100644 index 0000000..cc382f5 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithDisplayName.verified.txt @@ -0,0 +1,194 @@ +//------------------------------------------------------------------------------ +// +// 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 MyTestNameSpace +{ + public static partial class MyEnumExtensions + { + public static string ToStringFast(this MyTestNameSpace.MyEnum value) + => value switch + { + MyTestNameSpace.MyEnum.First => nameof(MyTestNameSpace.MyEnum.First), + MyTestNameSpace.MyEnum.Second => "2nd", + MyTestNameSpace.MyEnum.Third => nameof(MyTestNameSpace.MyEnum.Third), + MyTestNameSpace.MyEnum.Fourth => "4th", + _ => value.ToString(), + }; + + public static bool IsDefined(MyTestNameSpace.MyEnum value) + => value switch + { + MyTestNameSpace.MyEnum.First => true, + MyTestNameSpace.MyEnum.Second => true, + MyTestNameSpace.MyEnum.Third => true, + MyTestNameSpace.MyEnum.Fourth => true, + _ => false, + }; + + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + var isDefinedInDisplayAttribute = false; + if (allowMatchingMetadataAttribute) + { + isDefinedInDisplayAttribute = name switch + { + "2nd" => true, + "4th" => true, + _ => false, + }; + } + + if (isDefinedInDisplayAttribute) + { + return true; + } + + + return name switch + { + nameof(MyTestNameSpace.MyEnum.First) => true, + nameof(MyTestNameSpace.MyEnum.Second) => true, + nameof(MyTestNameSpace.MyEnum.Third) => true, + nameof(MyTestNameSpace.MyEnum.Fourth) => true, + _ => false, + }; + } + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out MyTestNameSpace.MyEnum value) + => TryParse(name, out value, false, false); + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out MyTestNameSpace.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out MyTestNameSpace.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + { + if (allowMatchingMetadataAttribute) + { + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals("2nd", System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when s.Equals("4th", System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Fourth; + return true; + default: + break; + }; + } + else + { + switch (name) + { + case "2nd": + value = MyTestNameSpace.MyEnum.Second; + return true; + case "4th": + value = MyTestNameSpace.MyEnum.Fourth; + return true; + default: + break; + }; + } + } + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.First; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Third), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Third; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Fourth), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Fourth; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else + { + switch (name) + { + case nameof(MyTestNameSpace.MyEnum.First): + value = MyTestNameSpace.MyEnum.First; + return true; + case nameof(MyTestNameSpace.MyEnum.Second): + value = MyTestNameSpace.MyEnum.Second; + return true; + case nameof(MyTestNameSpace.MyEnum.Third): + value = MyTestNameSpace.MyEnum.Third; + return true; + case nameof(MyTestNameSpace.MyEnum.Fourth): + value = MyTestNameSpace.MyEnum.Fourth; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + } + + public static MyTestNameSpace.MyEnum[] GetValues() + { + return new[] + { + MyTestNameSpace.MyEnum.First, + MyTestNameSpace.MyEnum.Second, + MyTestNameSpace.MyEnum.Third, + MyTestNameSpace.MyEnum.Fourth, + }; + } + + public static string[] GetNames() + { + return new[] + { + nameof(MyTestNameSpace.MyEnum.First), + nameof(MyTestNameSpace.MyEnum.Second), + nameof(MyTestNameSpace.MyEnum.Third), + nameof(MyTestNameSpace.MyEnum.Fourth), + }; + } + } +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithSameDisplayName.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithSameDisplayName.verified.txt new file mode 100644 index 0000000..3b4852f --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/EnumGeneratorTests.CanGenerateEnumExtensionsWithSameDisplayName.verified.txt @@ -0,0 +1,187 @@ +//------------------------------------------------------------------------------ +// +// 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 MyTestNameSpace +{ + public static partial class MyEnumExtensions + { + public static string ToStringFast(this MyTestNameSpace.MyEnum value) + => value switch + { + MyTestNameSpace.MyEnum.First => nameof(MyTestNameSpace.MyEnum.First), + MyTestNameSpace.MyEnum.Second => "2nd", + MyTestNameSpace.MyEnum.Third => nameof(MyTestNameSpace.MyEnum.Third), + MyTestNameSpace.MyEnum.Fourth => "2nd", + _ => value.ToString(), + }; + + public static bool IsDefined(MyTestNameSpace.MyEnum value) + => value switch + { + MyTestNameSpace.MyEnum.First => true, + MyTestNameSpace.MyEnum.Second => true, + MyTestNameSpace.MyEnum.Third => true, + MyTestNameSpace.MyEnum.Fourth => true, + _ => false, + }; + + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + var isDefinedInDisplayAttribute = false; + if (allowMatchingMetadataAttribute) + { + isDefinedInDisplayAttribute = name switch + { + "2nd" => true, + _ => false, + }; + } + + if (isDefinedInDisplayAttribute) + { + return true; + } + + + return name switch + { + nameof(MyTestNameSpace.MyEnum.First) => true, + nameof(MyTestNameSpace.MyEnum.Second) => true, + nameof(MyTestNameSpace.MyEnum.Third) => true, + nameof(MyTestNameSpace.MyEnum.Fourth) => true, + _ => false, + }; + } + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out MyTestNameSpace.MyEnum value) + => TryParse(name, out value, false, false); + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out MyTestNameSpace.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); + + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out MyTestNameSpace.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + { + if (allowMatchingMetadataAttribute) + { + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals("2nd", System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + default: + break; + }; + } + else + { + switch (name) + { + case "2nd": + value = MyTestNameSpace.MyEnum.Second; + return true; + default: + break; + }; + } + } + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.First), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.First; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Second), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Second; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Third), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Third; + return true; + case string s when s.Equals(nameof(MyTestNameSpace.MyEnum.Fourth), System.StringComparison.OrdinalIgnoreCase): + value = MyTestNameSpace.MyEnum.Fourth; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + else + { + switch (name) + { + case nameof(MyTestNameSpace.MyEnum.First): + value = MyTestNameSpace.MyEnum.First; + return true; + case nameof(MyTestNameSpace.MyEnum.Second): + value = MyTestNameSpace.MyEnum.Second; + return true; + case nameof(MyTestNameSpace.MyEnum.Third): + value = MyTestNameSpace.MyEnum.Third; + return true; + case nameof(MyTestNameSpace.MyEnum.Fourth): + value = MyTestNameSpace.MyEnum.Fourth; + return true; + case string s when int.TryParse(name, out var val): + value = (MyTestNameSpace.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + } + + public static MyTestNameSpace.MyEnum[] GetValues() + { + return new[] + { + MyTestNameSpace.MyEnum.First, + MyTestNameSpace.MyEnum.Second, + MyTestNameSpace.MyEnum.Third, + MyTestNameSpace.MyEnum.Fourth, + }; + } + + public static string[] GetNames() + { + return new[] + { + nameof(MyTestNameSpace.MyEnum.First), + nameof(MyTestNameSpace.MyEnum.Second), + nameof(MyTestNameSpace.MyEnum.Third), + nameof(MyTestNameSpace.MyEnum.Fourth), + }; + } + } +} \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesEnumCorrectly.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesEnumCorrectly.verified.txt index 8b1718d..a7313b9 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesEnumCorrectly.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesEnumCorrectly.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -28,68 +28,78 @@ namespace Something.Blah _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(Something.Blah.ShortName.First) => true, nameof(Something.Blah.ShortName.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out Something.Blah.ShortName value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out Something.Blah.ShortName value) - { - switch (name) - { - case { } s when s.Equals(nameof(Something.Blah.ShortName.First), System.StringComparison.OrdinalIgnoreCase): - value = Something.Blah.ShortName.First; - return true; - case { } s when s.Equals(nameof(Something.Blah.ShortName.Second), System.StringComparison.OrdinalIgnoreCase): - value = Something.Blah.ShortName.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (Something.Blah.ShortName)val; - return true; - default: - value = default; - return false; - } - } + out Something.Blah.ShortName value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out Something.Blah.ShortName value) + out Something.Blah.ShortName value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(Something.Blah.ShortName.First), System.StringComparison.OrdinalIgnoreCase): + value = Something.Blah.ShortName.First; + return true; + case string s when s.Equals(nameof(Something.Blah.ShortName.Second), System.StringComparison.OrdinalIgnoreCase): + value = Something.Blah.ShortName.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(Something.Blah.ShortName.First): - value = Something.Blah.ShortName.First; - return true; - case nameof(Something.Blah.ShortName.Second): - value = Something.Blah.ShortName.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (Something.Blah.ShortName)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(Something.Blah.ShortName.First): + value = Something.Blah.ShortName.First; + return true; + case nameof(Something.Blah.ShortName.Second): + value = Something.Blah.ShortName.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly.verified.txt index 51a1635..0baff5a 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly.verified.txt +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/SourceGenerationHelperSnapshotTests.GeneratesFlagsEnumCorrectly.verified.txt @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by the NetEscapades.EnumGenerators source generator // @@ -35,68 +35,78 @@ namespace Something.Blah _ => false, }; - public static bool IsDefined(string name) - => name switch + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch { nameof(Something.Blah.ShortName.First) => true, nameof(Something.Blah.ShortName.Second) => true, _ => false, }; + } public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - bool ignoreCase, out Something.Blah.ShortName value) - => ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value); + => TryParse(name, out value, false, false); - private static bool TryParseIgnoreCase( + public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out Something.Blah.ShortName value) - { - switch (name) - { - case { } s when s.Equals(nameof(Something.Blah.ShortName.First), System.StringComparison.OrdinalIgnoreCase): - value = Something.Blah.ShortName.First; - return true; - case { } s when s.Equals(nameof(Something.Blah.ShortName.Second), System.StringComparison.OrdinalIgnoreCase): - value = Something.Blah.ShortName.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (Something.Blah.ShortName)val; - return true; - default: - value = default; - return false; - } - } + out Something.Blah.ShortName value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); public static bool TryParse( #if NETCOREAPP3_0_OR_GREATER [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? name, - out Something.Blah.ShortName value) + out Something.Blah.ShortName value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) { - switch (name) + + if (ignoreCase) + { + switch (name) + { + case string s when s.Equals(nameof(Something.Blah.ShortName.First), System.StringComparison.OrdinalIgnoreCase): + value = Something.Blah.ShortName.First; + return true; + case string s when s.Equals(nameof(Something.Blah.ShortName.Second), System.StringComparison.OrdinalIgnoreCase): + value = Something.Blah.ShortName.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } + } + else { - case nameof(Something.Blah.ShortName.First): - value = Something.Blah.ShortName.First; - return true; - case nameof(Something.Blah.ShortName.Second): - value = Something.Blah.ShortName.Second; - return true; - case { } s when int.TryParse(name, out var val): - value = (Something.Blah.ShortName)val; - return true; - default: - value = default; - return false; + switch (name) + { + case nameof(Something.Blah.ShortName.First): + value = Something.Blah.ShortName.First; + return true; + case nameof(Something.Blah.ShortName.Second): + value = Something.Blah.ShortName.Second; + return true; + case string s when int.TryParse(name, out var val): + value = (Something.Blah.ShortName)val; + return true; + default: + value = default; + return false; + } } } diff --git a/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs b/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs index d30e686..4966ee1 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs +++ b/tests/NetEscapades.EnumGenerators.Tests/SourceGenerationHelperSnapshotTests.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -20,8 +19,13 @@ public Task GeneratesEnumCorrectly() "Something.Blah.ShortName", "int", isPublic: true, - new Dictionary { { "First", 0 }, { "Second", 1 } }.ToList(), - hasFlags: false); + new Dictionary + { + { "First", new EnumValueOption(null, false) }, + { "Second", new EnumValueOption(null, false) } + }.ToList(), + hasFlags: false, + isDisplaAttributeUsed: false); var sb = new StringBuilder(); var result = SourceGenerationHelper.GenerateExtensionClass(sb, value); @@ -39,8 +43,13 @@ public Task GeneratesFlagsEnumCorrectly() "Something.Blah.ShortName", "int", isPublic: true, - new Dictionary { { "First", 0 }, { "Second", 1 } }.ToList(), - hasFlags: true); + new Dictionary + { + { "First", new EnumValueOption(null, false) }, + { "Second", new EnumValueOption(null, false) } + }.ToList(), + hasFlags: true, + isDisplaAttributeUsed: false); var sb = new StringBuilder(); var result = SourceGenerationHelper.GenerateExtensionClass(sb, value); diff --git a/tests/NetEscapades.EnumGenerators.Tests/TestHelpers.cs b/tests/NetEscapades.EnumGenerators.Tests/TestHelpers.cs index 0e6a186..e91133b 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/TestHelpers.cs +++ b/tests/NetEscapades.EnumGenerators.Tests/TestHelpers.cs @@ -18,7 +18,10 @@ public static (ImmutableArray Diagnostics, string Output) GetGenerat .Concat(new[] { MetadataReference.CreateFromFile(typeof(T).Assembly.Location), - MetadataReference.CreateFromFile(typeof(EnumExtensionsAttribute).Assembly.Location) + MetadataReference.CreateFromFile(typeof(EnumExtensionsAttribute).Assembly.Location), + #if NET48 + MetadataReference.CreateFromFile(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute).Assembly.Location), + #endif }); var compilation = CSharpCompilation.Create(