Skip to content

Instantly share code, notes, and snippets.

@Meir017
Created August 7, 2024 17:48
Show Gist options
  • Save Meir017/076cc5d2a96e2b1b12c190f3a48befc9 to your computer and use it in GitHub Desktop.
Save Meir017/076cc5d2a96e2b1b12c190f3a48befc9 to your computer and use it in GitHub Desktop.
source generator for Garnet Enums
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);
}
}
}
}
[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