Created
August 7, 2024 17:48
-
-
Save Meir017/076cc5d2a96e2b1b12c190f3a48befc9 to your computer and use it in GitHub Desktop.
source generator for Garnet Enums
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text; | |
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | |
namespace dotnet_analyzer_ideas; | |
[Generator] | |
public class EnumsSourceGenerator : ISourceGenerator | |
{ | |
const string GeneratedClassName = "EnumUtils"; | |
public void Initialize(GeneratorInitializationContext context) | |
{ | |
context.RegisterForSyntaxNotifications(() => new EnumsSyntaxReceiver()); | |
} | |
public void Execute(GeneratorExecutionContext context) | |
{ | |
var syntaxReceiver = context.SyntaxReceiver as EnumsSyntaxReceiver; | |
if (syntaxReceiver is null) return; | |
foreach (var enumDeclaration in syntaxReceiver.Enums) | |
{ | |
var enumName = enumDeclaration.Identifier.Text; | |
var values = enumDeclaration.Members | |
.Select(m => ( | |
m.Identifier.Text, | |
Value: m.EqualsValue?.Value?.ToString(), | |
Descriptions: m.AttributeLists | |
.SelectMany(al => al.Attributes).Where(a => a.Name.ToString() == "Description") | |
)) | |
.ToList(); | |
var classBuilder = new StringBuilder(); | |
classBuilder.AppendLine("// <auto-generated>"); | |
classBuilder.AppendLine($"// This code was generated by the {nameof(EnumsSourceGenerator)} source generator."); | |
classBuilder.AppendLine("using System;"); | |
classBuilder.AppendLine("using System.ComponentModel;"); | |
classBuilder.AppendLine(); | |
classBuilder.AppendLine("namespace dotnet_ideas;"); | |
classBuilder.AppendLine(); | |
classBuilder.AppendLine($"public static partial class {GeneratedClassName}"); | |
classBuilder.AppendLine("{"); | |
classBuilder.AppendLine(GenerateTryParseEnumFromDescriptionMethod(enumName, values)); | |
classBuilder.AppendLine(); | |
classBuilder.AppendLine(GenerateGetEnumDescriptionsMethod(enumName, values)); | |
classBuilder.AppendLine("}"); | |
var classSource = classBuilder.ToString(); | |
context.AddSource($"{GeneratedClassName}.{enumName}.g.cs", classSource); | |
} | |
context.AddSource($"{GeneratedClassName}.g.cs", $@"namespace dotnet_ideas; | |
public static partial class {GeneratedClassName} | |
{{ | |
}}"); | |
} | |
private static string GenerateTryParseEnumFromDescriptionMethod(string enumName, List<(string Name, string? Value, IEnumerable<AttributeSyntax> Descriptions)> values) | |
{ | |
var method = new StringBuilder(); | |
method.AppendLine($" public static bool TryParse{enumName}FromDescription(string description, out {enumName} result)"); | |
method.AppendLine(" {"); | |
method.AppendLine(" result = default;"); | |
method.AppendLine(" switch (description)"); | |
method.AppendLine(" {"); | |
foreach (var (name, _, descriptions) in values) | |
{ | |
bool hasDescription = false; | |
foreach (var description in descriptions) | |
{ | |
var descriptionValue = description.ArgumentList?.Arguments.FirstOrDefault()?.ToString(); | |
if (descriptionValue is not null) | |
{ | |
hasDescription = true; | |
method.AppendLine($" case {descriptionValue}:"); | |
} | |
} | |
if (!hasDescription) continue; | |
method.AppendLine($" result = {enumName}.{name};"); | |
method.AppendLine(" return true;"); | |
} | |
method.AppendLine(" }"); | |
method.AppendLine(); | |
method.AppendLine(" return false;"); | |
method.AppendLine(" }"); | |
method.AppendLine(); | |
return method.ToString(); | |
} | |
private static string GenerateGetEnumDescriptionsMethod(string enumName, List<(string Name, string? Value, IEnumerable<AttributeSyntax> Descriptions)> values) | |
{ | |
var method = new StringBuilder(); | |
method.AppendLine($" public static string[] Get{enumName}Descriptions({enumName} value)"); | |
method.AppendLine(" {"); | |
method.AppendLine(" return value switch"); | |
method.AppendLine(" {"); | |
foreach (var (name, _, descriptions) in values) | |
{ | |
foreach (var description in descriptions) | |
{ | |
var descriptionValue = description.ArgumentList?.Arguments.FirstOrDefault()?.ToString(); | |
if (descriptionValue is not null) | |
{ | |
method.AppendLine($" {enumName}.{name} => [ {string.Join(", ", descriptions.Select(d => d.ArgumentList?.Arguments.FirstOrDefault()?.ToString()))} ],"); | |
} | |
} | |
} | |
method.AppendLine(" };"); | |
method.AppendLine(" }"); | |
return method.ToString(); | |
} | |
private class EnumsSyntaxReceiver : ISyntaxReceiver | |
{ | |
public List<EnumDeclarationSyntax> Enums { get; } = new(); | |
public void OnVisitSyntaxNode(SyntaxNode syntaxNode) | |
{ | |
if (syntaxNode is EnumDeclarationSyntax enumDeclarationSyntax | |
&& enumDeclarationSyntax.AttributeLists.Any(al => al.Attributes.Any(a => a.Name.ToString() == "GenerateEnumUtils"))) | |
{ | |
Enums.Add(enumDeclarationSyntax); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[AttributeUsage(AttributeTargets.Enum, AllowMultiple = false)] | |
public class GenerateEnumUtilsAttribute : Attribute | |
{ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment