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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion Supernova.Enum.Generators/EnumSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute
/**********************/
var enumDisplayNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var enumDescriptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var enumShortNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

foreach (var member in enumSymbol.GetMembers())
{
if (member is not IFieldSymbol field
Expand All @@ -103,7 +105,11 @@ public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute
{
enumDescriptions.Add(member.Name, description);
}

if (namedArgument.Key.Equals("ShortName", StringComparison.OrdinalIgnoreCase) &&
Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting the string literal "ShortName" into a constant to improve maintainability and avoid potential typos.

Suggested change
if (namedArgument.Key.Equals("ShortName", StringComparison.OrdinalIgnoreCase) &&
if (namedArgument.Key.Equals(ShortNameKey, StringComparison.OrdinalIgnoreCase) &&

Copilot uses AI. Check for mistakes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggestion does not match the rest of the conventions in the if statements around it

namedArgument.Value.Value?.ToString() is { } shortName)
{
enumShortNames.Add(member.Name, shortName);
}
}
}
}
Expand All @@ -126,6 +132,9 @@ namespace {SourceGeneratorHelper.NameSpace}
//DisplayNames Dictionary
DisplayNamesDictionary(sourceBuilder, symbol, e, enumDisplayNames);

//DisplayShortNames Dictionary
DisplayShortNamesDictionary(sourceBuilder, symbol, e, enumShortNames);

//DisplayDescriptions Dictionary
DisplayDescriptionsDictionary(sourceBuilder, symbol, e, enumDescriptions);

Expand All @@ -144,6 +153,9 @@ namespace {SourceGeneratorHelper.NameSpace}
//ToDisplay string
ToDescription(sourceBuilder, symbol, e, enumDescriptions);

//ToShortName
ToShortName(sourceBuilder, symbol, e, enumShortNames);

//GetValues
GetValuesFast(sourceBuilder, symbol, e);

Expand Down Expand Up @@ -199,6 +211,37 @@ private static void ToDisplay(StringBuilder sourceBuilder, ISymbol symbol, EnumD
");
}

private static void ToShortName(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e, Dictionary<string, string> enumShortNames)
{
sourceBuilder.Append($@"
/// <summary>
/// Converts the <see cref=""global::{symbol.FullName()}"" /> enumeration value to its short name string.
/// </summary>
/// <param name=""states"">The <see cref=""global::{symbol.FullName()}"" /> enumeration value.</param>
/// <param name=""defaultValue"">The default value to return if the enumeration value is not recognized.</param>
/// <returns>The short name string of the <see cref=""global::{symbol.FullName()}"" /> value.</returns>
public static string {SourceGeneratorHelper.ExtensionMethodNameToShortName}(this {symbol.FullName()} states, string defaultValue = null)
{{
return states switch
{{
");
foreach (var member in e.Members)
{
var key = member.Identifier.ValueText;
var enumShortName = enumShortNames.TryGetValue(key, out var found)
? found
: key;
sourceBuilder.AppendLine(
$@" {symbol}.{member.Identifier.ValueText} => ""{enumShortName}"",");
}

sourceBuilder.Append(
@" _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null)
};
}
");
}

private static void ToDescription(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e,
Dictionary<string, string> enumDescriptions)
{
Expand Down Expand Up @@ -348,6 +391,31 @@ private static void DisplayDescriptionsDictionary(StringBuilder sourceBuilder, I
");
}

private static void DisplayShortNamesDictionary(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e,
Dictionary<string, string> enumShortNames)
{
sourceBuilder.Append($@"
/// <summary>
/// Provides a dictionary that maps <see cref=""global::{symbol.FullName()}"" /> values to their corresponding short names.
/// </summary>
public static readonly ImmutableDictionary<{symbol.FullName()}, string> {SourceGeneratorHelper.PropertyDisplayShortNamesDictionary} = new Dictionary<{symbol.FullName()}, string>
{{
");
foreach (var member in e.Members)
{
var key = member.Identifier.ValueText;
var enumShortName = enumShortNames.TryGetValue(key, out var found)
? found
: key;
sourceBuilder.AppendLine(
$@" {{{symbol}.{member.Identifier.ValueText}, ""{enumShortName}""}},");
}
sourceBuilder.Append(
@"
}.ToImmutableDictionary();
");
}

private static void GetValuesFast(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e)
{
sourceBuilder.Append($@"
Expand Down
2 changes: 2 additions & 0 deletions Supernova.Enum.Generators/SourceGeneratorHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ public static class SourceGeneratorHelper
public const string ExtensionMethodNameIsDefined = "IsDefinedFast";
public const string ExtensionMethodNameToDisplay = "ToDisplayFast";
public const string ExtensionMethodNameToDescription = "ToDescriptionFast";
public const string ExtensionMethodNameToShortName = "ToShortNameFast";
public const string ExtensionMethodNameGetValues = "GetValuesFast";
public const string ExtensionMethodNameGetNames = "GetNamesFast";
public const string ExtensionMethodNameGetLength = "GetLengthFast";
public const string ExtensionMethodNameTryParse = "TryParseFast";
public const string PropertyDisplayNamesDictionary = "DisplayNamesDictionary";
public const string PropertyDisplayShortNamesDictionary = "DisplayShortNamesDictionary";
public const string PropertyDisplayDescriptionsDictionary = "DisplayDescriptionsDictionary";

}
29 changes: 27 additions & 2 deletions test/Console.Test.Benchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ namespace Console.Test.Benchmark;
[EnumGenerator]
public enum UserType
{
[Display(Name = "مرد", Description = "men")] Men,
[Display(Name = "مرد", Description = "men", ShortName = "M")] Men,

[Display(Name = "زن", Description = "women")] Women,
[Display(Name = "زن", Description = "women", ShortName = "W")] Women,

//[Display(Name = "نامشخص")]
None
Expand Down Expand Up @@ -97,6 +97,18 @@ public string FastToDisplay()
return UserType.Men.ToDisplayFast();
}

[Benchmark]
public string NativeToShortName()
{
return UserType.Men.ToShortNameNative();
}

[Benchmark]
public string FastToShortName()
{
return UserType.Men.ToShortNameFast();
}

[Benchmark]
public string NativeToDescription()
{
Expand Down Expand Up @@ -173,6 +185,19 @@ public static string ToDisplayNative(this Enum value)

return propValue?.ToString();
}

public static string ToShortNameNative(this Enum value)
{
if (value is null)
throw new ArgumentNullException(nameof(value));
var attribute = value.GetType().GetField(value.ToString())?
.GetCustomAttributes<DisplayAttribute>(false).FirstOrDefault();
if (attribute == null)
return value.ToString();
var propValue = attribute.GetType().GetProperty("ShortName")?.GetValue(attribute, null);
Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting the string literal "ShortName" into a constant to improve maintainability and avoid potential typos.

Suggested change
var propValue = attribute.GetType().GetProperty("ShortName")?.GetValue(attribute, null);
var propValue = attribute.GetType().GetProperty(ShortNameProperty)?.GetValue(attribute, null);

Copilot uses AI. Check for mistakes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggestion does not match the conventions of the tests around it

return propValue?.ToString();
}

public static string ToDescriptionNative(this Enum value)
{
if (value is null)
Expand Down
44 changes: 42 additions & 2 deletions test/UnitTests/EnumGeneratorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace UnitTests;
[EnumGenerator]
public enum UserTypeTest
{
[Display(Name = "مرد", Description = "Descمرد")] Men = 3,
[Display(Name = "مرد", Description = "Descمرد", ShortName = "M")] Men = 3,

[Display(Name = "زن", Description = "Descزن")] Women = 4,
[Display(Name = "زن", Description = "Descزن", ShortName = "W")] Women = 4,

//[Display(Name = "نامشخص")]
None
Expand Down Expand Up @@ -117,6 +117,32 @@ public void TestEnumToDisplay_Undefined_DefaultValue()
value.Should().Be("DefaultValue");
}

[TestMethod]
public void TestEnumToShortName()
{
var menShortName = UserTypeTest.Men.ToShortNameFast();
var womenShortName = UserTypeTest.Women.ToShortNameFast();
var noneShortName = UserTypeTest.None.ToShortNameFast();

menShortName.Should().Be("M");
womenShortName.Should().Be("W");
noneShortName.Should().Be("None");
}

[TestMethod]
public void TestEnumToShortName_Undefined()
{
var action = () => GetUndefinedEnumValue().ToShortNameFast();
action.Should().Throw<ArgumentOutOfRangeException>();
}

[TestMethod]
public void TestEnumToShortName_Undefined_DefaultValue()
{
var value = GetUndefinedEnumValue().ToShortNameFast("DefaultValue");
value.Should().Be("DefaultValue");
}

[TestMethod]
public void TestEnumGetNames()
{
Expand All @@ -142,6 +168,20 @@ public void TestEnumDisplayNamesDictionary()
new KeyValuePair<UserTypeTest, string>(UserTypeTest.None, "None")
);
}

[TestMethod]
public void TestEnumDisplayShortNamesDictionary()
{
var names = UserTypeTestEnumExtensions.DisplayShortNamesDictionary;
Assert.IsNotNull(names);
names.Should().NotBeEmpty()
.And.HaveCount(3)
.And.ContainInOrder(
new KeyValuePair<UserTypeTest, string>(UserTypeTest.Men, "M"),
new KeyValuePair<UserTypeTest, string>(UserTypeTest.Women, "W"),
new KeyValuePair<UserTypeTest, string>(UserTypeTest.None, "None")
);
}

[TestMethod]
public void TestEnumDisplayDescriptionsDictionary()
Expand Down
44 changes: 42 additions & 2 deletions test/UnitTests/InternalEnumGeneratorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace UnitTests;
[EnumGenerator]
internal enum InternalUserTypeTest
{
[Display(Name = "مرد", Description = "Descمرد")] Men = 3,
[Display(Name = "مرد", Description = "Descمرد", ShortName = "M")] Men = 3,

[Display(Name = "زن", Description = "Descزن")] Women = 4,
[Display(Name = "زن", Description = "Descزن", ShortName = "W")] Women = 4,

//[Display(Name = "نامشخص")]
None
Expand Down Expand Up @@ -117,6 +117,32 @@ public void TestEnumToDisplay_Undefined_DefaultValue()
value.Should().Be("DefaultValue");
}

[TestMethod]
public void TestEnumToShortName()
{
var menShortName = InternalUserTypeTest.Men.ToShortNameFast();
var womenShortName = InternalUserTypeTest.Women.ToShortNameFast();
var noneShortName = InternalUserTypeTest.None.ToShortNameFast();

menShortName.Should().Be("M");
womenShortName.Should().Be("W");
noneShortName.Should().Be("None");
}

[TestMethod]
public void TestEnumToShortName_Undefined()
{
var action = () => GetUndefinedEnumValue().ToShortNameFast();
action.Should().Throw<ArgumentOutOfRangeException>();
}

[TestMethod]
public void TestEnumToShortName_Undefined_DefaultValue()
{
var value = GetUndefinedEnumValue().ToShortNameFast("DefaultValue");
value.Should().Be("DefaultValue");
}

[TestMethod]
public void TestEnumGetNames()
{
Expand All @@ -142,6 +168,20 @@ public void TestEnumDisplayNamesDictionary()
new KeyValuePair<InternalUserTypeTest, string>(InternalUserTypeTest.None, "None")
);
}

[TestMethod]
public void TestEnumDisplayShortNamesDictionary()
{
var names = InternalUserTypeTestEnumExtensions.DisplayShortNamesDictionary;
Assert.IsNotNull(names);
names.Should().NotBeEmpty()
.And.HaveCount(3)
.And.ContainInOrder(
new KeyValuePair<InternalUserTypeTest, string>(InternalUserTypeTest.Men, "M"),
new KeyValuePair<InternalUserTypeTest, string>(InternalUserTypeTest.Women, "W"),
new KeyValuePair<InternalUserTypeTest, string>(InternalUserTypeTest.None, "None")
);
}

[TestMethod]
public void TestEnumDisplayDescriptionsDictionary()
Expand Down