Skip to content

Commit

Permalink
Merge pull request #65 from oguzhankahyaoglu/features/default-values-…
Browse files Browse the repository at this point in the history
…name-description-dictionaries

Default values for ToStringFast/ToDisplayFast methods, added ToDescriptionFast method. Added two other dictionaries for holding static DisplayName/Descriptions
  • Loading branch information
EngRajabi authored Feb 23, 2024
2 parents eb53599 + 756fe94 commit dca63a4
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 29 deletions.
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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[]
Expand Down
118 changes: 102 additions & 16 deletions Supernova.Enum.Generators/EnumSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute


/**********************/
var memberAttribute = new Dictionary<string, string>();
var enumDisplayNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var enumDescriptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var member in enumSymbol.GetMembers())
{
if (member is not IFieldSymbol field
Expand All @@ -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);

Expand All @@ -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);
Expand All @@ -131,28 +150,51 @@ public static class {symbol.Name}EnumExtensions
}

private static void ToDisplay(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e,
Dictionary<string, string> memberAttribute)
Dictionary<string, string> 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<string, string> 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)
};
}");
}
Expand Down Expand Up @@ -192,19 +234,63 @@ 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
{{
");
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<string, string> 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<string, string> 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($@"
Expand Down
3 changes: 3 additions & 0 deletions Supernova.Enum.Generators/SourceGeneratorHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 + $@"
Expand Down
10 changes: 7 additions & 3 deletions Supernova.Enum.Generators/Supernova.Enum.Generators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
<PackageId>Supernova.Enum.Generators</PackageId>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Version>1.0.13</Version>
<PackageVersion>1.0.13</PackageVersion>
<AssemblyVersion>1.0.13</AssemblyVersion>
<Version>1.0.14</Version>
<PackageVersion>1.0.14</PackageVersion>
<AssemblyVersion>1.0.14</AssemblyVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageIcon>icon.png</PackageIcon>
<PackageReleaseNotes>
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
Expand Down
73 changes: 71 additions & 2 deletions test/UnitTests/EnumGeneratorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

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
Expand Down Expand Up @@ -52,6 +53,38 @@ public void TestEnumToString_Undefined()
action.Should().Throw<ArgumentOutOfRangeException>();
}

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

[TestMethod]
public void TestEnumToDescription_Undefined_DefaultValue()
{
var value = GetUndefinedEnumValue().ToDescriptionFast("DefaultValue");

value.Should().Be("DefaultValue");
}

[TestMethod]
public void TestEnumToDisplay()
{
Expand All @@ -76,6 +109,14 @@ public void TestEnumToDisplay_Undefined()
action.Should().Throw<ArgumentOutOfRangeException>();
}

[TestMethod]
public void TestEnumToDisplay_Undefined_DefaultValue()
{
var value = GetUndefinedEnumValue().ToDisplayFast("DefaultValue");

value.Should().Be("DefaultValue");
}

[TestMethod]
public void TestEnumGetNames()
{
Expand All @@ -88,6 +129,34 @@ public void TestEnumGetNames()
.And.ContainItemsAssignableTo<string>();
}

[TestMethod]
public void TestEnumDisplayNamesDictionary()
{
var names = UserTypeTestEnumExtensions.DisplayNamesDictionary;
Assert.IsNotNull(names);
names.Should().NotBeEmpty()
.And.HaveCount(3)
.And.ContainInOrder(
new KeyValuePair<UserTypeTest, string>(UserTypeTest.Men, "مرد"),
new KeyValuePair<UserTypeTest, string>(UserTypeTest.Women, "زن"),
new KeyValuePair<UserTypeTest, string>(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, string>(UserTypeTest.Men, "Descمرد"),
new KeyValuePair<UserTypeTest, string>(UserTypeTest.Women, "Descزن"),
new KeyValuePair<UserTypeTest, string>(UserTypeTest.None, "None")
);
}

[TestMethod]
public void TestEnumGetValues()
{
Expand Down

0 comments on commit dca63a4

Please sign in to comment.