diff --git a/README.md b/README.md index 5fc0da0..a33d333 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,9 @@ To use the generator, add the `[EnumGenerator]` attribute to an enum. For exampl [EnumGenerator] public enum UserTypeTest { - [Display(Name = "مرد")] - Men, + [Display(Name = "مرد", Description = "Descمرد")] Men = 3, - [Display(Name = "زن")] - Women, + [Display(Name = "زن", Description = "Descزن")] Women = 4, //[Display(Name = "نامشخص")] None @@ -47,14 +45,14 @@ For example: ```csharp public static class UserTypeTestEnumExtensions { - public static string ToStringFast(this UnitTests.UserTypeTest states) + public static string ToStringFast(this UnitTests.UserTypeTest states, string defaultValue = null) { return states switch { UnitTests.UserTypeTest.Men => nameof(UnitTests.UserTypeTest.Men), UnitTests.UserTypeTest.Women => nameof(UnitTests.UserTypeTest.Women), UnitTests.UserTypeTest.None => nameof(UnitTests.UserTypeTest.None), - _ => throw new ArgumentOutOfRangeException(nameof(states), states, null) + _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null) }; } public static bool IsDefinedFast(UnitTests.UserTypeTest states) @@ -77,16 +75,28 @@ For example: _ => false }; } - public static string ToDisplayFast(this UnitTests.UserTypeTest states) + public static string ToDisplayFast(this UnitTests.UserTypeTest states, string defaultValue = null) { return states switch { UnitTests.UserTypeTest.Men => "مرد", UnitTests.UserTypeTest.Women => "زن", UnitTests.UserTypeTest.None => "None", - _ => throw new ArgumentOutOfRangeException(nameof(states), states, null) + _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null) }; } + + public static string ToDescriptionFast(this UnitTests.UserTypeTest states, string defaultValue = null) + { + return states switch + { + UnitTests.UserTypeTest.Men => "Descمرد", + UnitTests.UserTypeTest.Women => "Descزن", + UnitTests.UserTypeTest.None => "None", + _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null) + }; + } + public static UnitTests.UserTypeTest[] GetValuesFast() { return new[] diff --git a/Supernova.Enum.Generators/EnumSourceGenerator.cs b/Supernova.Enum.Generators/EnumSourceGenerator.cs index da5bde8..b6de88b 100644 --- a/Supernova.Enum.Generators/EnumSourceGenerator.cs +++ b/Supernova.Enum.Generators/EnumSourceGenerator.cs @@ -71,7 +71,8 @@ public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute /**********************/ - var memberAttribute = new Dictionary(); + var enumDisplayNames = new Dictionary(StringComparer.OrdinalIgnoreCase); + var enumDescriptions = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var member in enumSymbol.GetMembers()) { if (member is not IFieldSymbol field @@ -84,21 +85,36 @@ public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute attribute.AttributeClass.Name != "DisplayAttribute") continue; foreach (var namedArgument in attribute.NamedArguments) + { if (namedArgument.Key.Equals("Name", StringComparison.OrdinalIgnoreCase) && - namedArgument.Value.Value?.ToString() is { } dn) + namedArgument.Value.Value?.ToString() is { } displayName) { - memberAttribute.Add(member.Name, dn); - break; + enumDisplayNames.Add(member.Name, displayName); + } + if (namedArgument.Key.Equals("Description", StringComparison.OrdinalIgnoreCase) && + namedArgument.Value.Value?.ToString() is { } description) + { + enumDescriptions.Add(member.Name, description); } + + } } } var sourceBuilder = new StringBuilder($@"using System; +using System.Collections.Generic; +using System.Collections.Immutable; namespace {SourceGeneratorHelper.NameSpace} {{ public static class {symbol.Name}EnumExtensions {{"); + //DisplayNames Dictionary + DisplayNamesDictionary(sourceBuilder, symbolName, e, enumDisplayNames); + + //DisplayDescriptions Dictionary + DisplayDescriptionsDictionary(sourceBuilder, symbolName, e, enumDescriptions); + //ToStringFast ToStringFast(sourceBuilder, symbolName, e); @@ -109,7 +125,10 @@ public static class {symbol.Name}EnumExtensions IsDefinedString(sourceBuilder, e, symbolName); //ToDisplay string - ToDisplay(sourceBuilder, symbolName, e, memberAttribute); + ToDisplay(sourceBuilder, symbolName, e, enumDisplayNames); + + //ToDisplay string + ToDescription(sourceBuilder, symbolName, e, enumDescriptions); //GetValues GetValuesFast(sourceBuilder, symbolName, e); @@ -131,28 +150,51 @@ public static class {symbol.Name}EnumExtensions } private static void ToDisplay(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e, - Dictionary memberAttribute) + Dictionary enumDisplayNames) { sourceBuilder.Append($@" - public static string {SourceGeneratorHelper.ExtensionMethodNameToDisplay}(this {symbolName} states) + public static string {SourceGeneratorHelper.ExtensionMethodNameToDisplay}(this {symbolName} states, string defaultValue = null) {{ return states switch {{ "); foreach (var member in e.Members) { - var display = memberAttribute - .FirstOrDefault(r => - r.Key.Equals(member.Identifier.ValueText, StringComparison.OrdinalIgnoreCase)) - .Value - ?? member.Identifier.ValueText; + var key = member.Identifier.ValueText; + var enumDisplayName = enumDisplayNames.TryGetValue(key, out var found) + ? found + : key; + sourceBuilder.AppendLine( + $@" {symbolName}.{member.Identifier.ValueText} => ""{enumDisplayName ?? key}"","); + } + + sourceBuilder.Append( + @" _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null) + }; + }"); + } + private static void ToDescription(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e, + Dictionary enumDescriptions) + { + sourceBuilder.Append($@" + public static string {SourceGeneratorHelper.ExtensionMethodNameToDescription}(this {symbolName} states, string defaultValue = null) + {{ + return states switch + {{ +"); + foreach (var member in e.Members) + { + var key = member.Identifier.ValueText; + var enumDescription = enumDescriptions.TryGetValue(key, out var found) + ? found + : key; sourceBuilder.AppendLine( - $@" {symbolName}.{member.Identifier.ValueText} => ""{display}"","); + $@" {symbolName}.{member.Identifier.ValueText} => ""{enumDescription ?? key}"","); } sourceBuilder.Append( - @" _ => throw new ArgumentOutOfRangeException(nameof(states), states, null) + @" _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null) }; }"); } @@ -192,7 +234,7 @@ private static void IsDefinedEnum(StringBuilder sourceBuilder, string symbolName private static void ToStringFast(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e) { sourceBuilder.Append($@" - public static string {SourceGeneratorHelper.ExtensionMethodNameToString}(this {symbolName} states) + public static string {SourceGeneratorHelper.ExtensionMethodNameToString}(this {symbolName} states, string defaultValue = null) {{ return states switch {{ @@ -200,11 +242,55 @@ private static void ToStringFast(StringBuilder sourceBuilder, string symbolName, foreach (var member in e.Members.Select(x => x.Identifier.ValueText)) sourceBuilder.AppendLine($@" {symbolName}.{member} => nameof({symbolName}.{member}),"); sourceBuilder.Append( - @" _ => throw new ArgumentOutOfRangeException(nameof(states), states, null) + @" _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null) }; }"); } + private static void DisplayNamesDictionary(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e, + Dictionary enumDisplayNames) + { + sourceBuilder.Append($@" + public static readonly ImmutableDictionary<{symbolName}, string> {SourceGeneratorHelper.PropertyDisplayNamesDictionary} = new Dictionary<{symbolName}, string> + {{ +"); + foreach (var member in e.Members) + { + var key = member.Identifier.ValueText; + var enumDisplayName = enumDisplayNames.TryGetValue(key, out var found) + ? found + : key; + sourceBuilder.AppendLine( + $@" {{{symbolName}.{member.Identifier.ValueText}, ""{enumDisplayName ?? key}""}},"); + } + sourceBuilder.Append( + @" + }.ToImmutableDictionary(); +"); + } + + private static void DisplayDescriptionsDictionary(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e, + Dictionary enumDescriptionNames) + { + sourceBuilder.Append($@" + public static readonly ImmutableDictionary<{symbolName}, string> {SourceGeneratorHelper.PropertyDisplayDescriptionsDictionary} = new Dictionary<{symbolName}, string> + {{ +"); + foreach (var member in e.Members) + { + var key = member.Identifier.ValueText; + var enumDescription = enumDescriptionNames.TryGetValue(key, out var found) + ? found + : key; + sourceBuilder.AppendLine( + $@" {{{symbolName}.{member.Identifier.ValueText}, ""{enumDescription ?? key}""}},"); + } + sourceBuilder.Append( + @" + }.ToImmutableDictionary(); +"); + } + private static void GetValuesFast(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e) { sourceBuilder.Append($@" diff --git a/Supernova.Enum.Generators/SourceGeneratorHelper.cs b/Supernova.Enum.Generators/SourceGeneratorHelper.cs index d770a80..5d49032 100644 --- a/Supernova.Enum.Generators/SourceGeneratorHelper.cs +++ b/Supernova.Enum.Generators/SourceGeneratorHelper.cs @@ -16,9 +16,12 @@ public static class SourceGeneratorHelper public const string ExtensionMethodNameToString = "ToStringFast"; public const string ExtensionMethodNameIsDefined = "IsDefinedFast"; public const string ExtensionMethodNameToDisplay = "ToDisplayFast"; + public const string ExtensionMethodNameToDescription = "ToDescriptionFast"; public const string ExtensionMethodNameGetValues = "GetValuesFast"; public const string ExtensionMethodNameGetNames = "GetNamesFast"; public const string ExtensionMethodNameGetLength = "GetLengthFast"; + public const string PropertyDisplayNamesDictionary = "DisplayNamesDictionary"; + public const string PropertyDisplayDescriptionsDictionary = "DisplayDescriptionsDictionary"; public const string Attribute = Header + $@" diff --git a/Supernova.Enum.Generators/Supernova.Enum.Generators.csproj b/Supernova.Enum.Generators/Supernova.Enum.Generators.csproj index 040ccd6..2386001 100644 --- a/Supernova.Enum.Generators/Supernova.Enum.Generators.csproj +++ b/Supernova.Enum.Generators/Supernova.Enum.Generators.csproj @@ -5,12 +5,16 @@ Supernova.Enum.Generators true false - 1.0.13 - 1.0.13 - 1.0.13 + 1.0.14 + 1.0.14 + 1.0.14 README.md icon.png + v1.0.14 + Added default values for ToStringFast/ToDisplayFast methods. + Added ToDescriptionFast method. + Added "DisplayNames" and "DisplayDescriptions" dictionaries to hold static values as dictionaries. v1.0.13 Breaking changes - return false for not found enum to IsDefinedFast diff --git a/test/UnitTests/EnumGeneratorTest.cs b/test/UnitTests/EnumGeneratorTest.cs index a9164a9..c0e50bb 100644 --- a/test/UnitTests/EnumGeneratorTest.cs +++ b/test/UnitTests/EnumGeneratorTest.cs @@ -2,6 +2,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace UnitTests; @@ -9,9 +10,9 @@ namespace UnitTests; [EnumGenerator] public enum UserTypeTest { - [Display(Name = "مرد")] Men, + [Display(Name = "مرد", Description = "Descمرد")] Men = 3, - [Display(Name = "زن")] Women, + [Display(Name = "زن", Description = "Descزن")] Women = 4, //[Display(Name = "نامشخص")] None @@ -52,6 +53,38 @@ public void TestEnumToString_Undefined() action.Should().Throw(); } + [TestMethod] + public void TestEnumToString_Undefined_DefaultValue() + { + var value = GetUndefinedEnumValue().ToStringFast("DefaultValue"); + + value.Should().Be("DefaultValue"); + } + + [TestMethod] + public void TestEnumToDescription() + { + var menString = UserTypeTest.Men.ToDescriptionFast(); + + menString.Should().Be("Descمرد"); + } + + [TestMethod] + public void TestEnumToDescription_Undefined() + { + var action = () => GetUndefinedEnumValue().ToDescriptionFast(); + + action.Should().Throw(); + } + + [TestMethod] + public void TestEnumToDescription_Undefined_DefaultValue() + { + var value = GetUndefinedEnumValue().ToDescriptionFast("DefaultValue"); + + value.Should().Be("DefaultValue"); + } + [TestMethod] public void TestEnumToDisplay() { @@ -76,6 +109,14 @@ public void TestEnumToDisplay_Undefined() action.Should().Throw(); } + [TestMethod] + public void TestEnumToDisplay_Undefined_DefaultValue() + { + var value = GetUndefinedEnumValue().ToDisplayFast("DefaultValue"); + + value.Should().Be("DefaultValue"); + } + [TestMethod] public void TestEnumGetNames() { @@ -88,6 +129,34 @@ public void TestEnumGetNames() .And.ContainItemsAssignableTo(); } + [TestMethod] + public void TestEnumDisplayNamesDictionary() + { + var names = UserTypeTestEnumExtensions.DisplayNamesDictionary; + Assert.IsNotNull(names); + names.Should().NotBeEmpty() + .And.HaveCount(3) + .And.ContainInOrder( + new KeyValuePair(UserTypeTest.Men, "مرد"), + new KeyValuePair(UserTypeTest.Women, "زن"), + new KeyValuePair(UserTypeTest.None, "None") + ); + } + + [TestMethod] + public void TestEnumDisplayDescriptionsDictionary() + { + var names = UserTypeTestEnumExtensions.DisplayDescriptionsDictionary; + Assert.IsNotNull(names); + names.Should().NotBeEmpty() + .And.HaveCount(3) + .And.ContainInOrder( + new KeyValuePair(UserTypeTest.Men, "Descمرد"), + new KeyValuePair(UserTypeTest.Women, "Descزن"), + new KeyValuePair(UserTypeTest.None, "None") + ); + } + [TestMethod] public void TestEnumGetValues() {