Setup route map source generator

This commit is contained in:
2024-09-23 13:38:20 +02:00
parent 0899b2d63b
commit 3e3845d414
15 changed files with 618 additions and 10 deletions
@@ -0,0 +1,75 @@
namespace Net.Sdk.Web.Extensions.SourceGenerators;
public static class Constants
{
public const string Partial = "partial";
public const string Public = "public";
public const string Abstract = "abstract";
public const string Sealed = "sealed";
public const string Static = "static";
public const string StringType = "string";
public const string IntType = "int";
public const string ObjectType = "object";
public const string Default = "default";
public const string Namespace = "Net.Sdk.Web";
public const string UsingSystemThreading = "System.Threading";
public const string UsingMicrosoftAspNetCoreHttp = "Microsoft.AspNetCore.Http";
public const string UsingMicrosoftAspNetCoreRouting = "Microsoft.AspNetCore.Routing";
public const string UsingMicrosoftAspNetCoreBuilder = "Microsoft.AspNetCore.Builder";
public const string RouteFileName = "Route";
public const string RouteInterface = "IRoute";
public const string RouteTypeParameter = "TRequest";
public const string PreProcessRequestReturnType = "Task<TRequest?>";
public const string PreProcessRequestMethodName = "PreProcess";
public const string HandleRequestReturnType = "Task<Response>";
public const string HandleRequestMethodName = "HandleRequest";
public const string HttpContextTypeName = "HttpContext?";
public const string HttpContextParameterName = "context";
public const string RequestParameterName = "request";
public const string ResponseTypeName = "Response";
public const string IResultTypeName = "IResult";
public const string SuccessResponseTypeName = "SuccessResponse";
public const string FailureResponseTypeName = "FailureResponse";
public const string ContentResponseTypeName = "ContentResponse";
public const string JsonResponseTypeName = "JsonResponse";
public const string StatusCodePropertyName = "StatusCode";
public const string ErrorMessagePropertyName = "ErrorMessage";
public const string ContentStringPropertyName = "ContentString";
public const string ValuePropertyName = "Value";
public const string ContentTypePropertyName = "ContentType";
public const string OkSuccessResponse = "Ok";
public const string CreatedSuccessResponse = "Created";
public const string ContentContentResponse = "Content";
public const string JsonJsonResponse = "Json";
public const string PageContentResponse = "Page";
public const string BadRequestFailureResponse = "BadRequest";
public const string UnauthorizedFailureResponse = "Unauthorized";
public const string ForbiddenFailureResponse = "Forbidden";
public const string NotFoundFailureResponse = "NotFound";
public const string ServerErrorFailureResponse = "ServerError";
public const string GetResult = "GetResult";
public const string ContentParameterName = "content";
public const string ContentTypeParameterName = "contentType";
public const string ValueParameterName = "value";
public const string ErrorMessageParameterName = "errorMessage";
public const string CancellationTokenTypeName = "CancellationToken";
public const string CancellationTokenParameterName = "cancellationToken";
public const string GetAttributeName = "GenerateMapGetAttribute";
public const string GetAttributeShortName = "GenerateMapGet";
public const string PostAttributeName = "GenerateMapPostAttribute";
public const string PostAttributeShortName = "GenerateMapPost";
public const string PutAttributeName = "GenerateMapPutAttribute";
public const string PutAttributeShortName = "GenerateMapPut";
public const string DeleteAttributeName = "GenerateMapDeleteAttribute";
public const string DeleteAttributeShortName = "GenerateMapDelete";
public const string PatternPropertyName = "Pattern";
public const string EndpointRouteBuilderExtensionsName = "EndpointRouteBuilderExtensions";
public const string IEndpointRouteBuilderTypeName = "IEndpointRouteBuilder";
public const string BuilderParameterName = "builder";
}
@@ -0,0 +1,205 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Sybil;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Net.Sdk.Web.Extensions.SourceGenerators;
[Generator(LanguageNames.CSharp)]
#nullable enable
public class MapEndpointGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider(
predicate: static (s, _) => s is ClassDeclarationSyntax,
transform: static (ctx, _) => GetFilteredClassDeclarationSyntax(ctx)).Where(static c => c is not null);
var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses, (sourceProductionContext, tuple) => Execute(tuple.Left, tuple.Right, sourceProductionContext));
}
private static ClassDeclarationSyntax? GetFilteredClassDeclarationSyntax(GeneratorSyntaxContext context)
{
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
if (classDeclarationSyntax.AttributeLists
.SelectMany(l => l.Attributes)
.OfType<AttributeSyntax>()
.Any(s => s.Name.ToString() == Constants.GetAttributeName || s.Name.ToString() == Constants.GetAttributeShortName ||
s.Name.ToString() == Constants.PostAttributeName || s.Name.ToString() == Constants.PostAttributeShortName ||
s.Name.ToString() == Constants.PutAttributeName || s.Name.ToString() == Constants.PutAttributeShortName ||
s.Name.ToString() == Constants.DeleteAttributeName || s.Name.ToString() == Constants.DeleteAttributeShortName))
{
return classDeclarationSyntax;
}
return default;
}
private static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax?> classes, SourceProductionContext sourceProductionContext)
{
if (classes.IsDefaultOrEmpty)
{
return;
}
var maybeLanguageVersion = (compilation.SyntaxTrees.FirstOrDefault()?.Options as CSharpParseOptions)?.LanguageVersion;
if (!maybeLanguageVersion.HasValue)
{
return;
}
var languageVersion = maybeLanguageVersion.Value;
var builder = SyntaxBuilder.CreateCompilationUnit()
.WithUsing(Constants.UsingSystemThreading)
.WithUsing(Constants.UsingMicrosoftAspNetCoreRouting)
.WithUsing(Constants.UsingMicrosoftAspNetCoreHttp)
.WithUsing(Constants.UsingMicrosoftAspNetCoreBuilder);
var routeUsings = new HashSet<string>();
var namespaceBuilder = languageVersion >= LanguageVersion.CSharp10 ? SyntaxBuilder.CreateFileScopedNamespace(Constants.Namespace) : SyntaxBuilder.CreateNamespace(Constants.Namespace);
builder.WithNamespace(namespaceBuilder);
var extensionClassBuilder = SyntaxBuilder.CreateClass(Constants.EndpointRouteBuilderExtensionsName)
.WithModifiers($"{Constants.Public} {Constants.Static}");
namespaceBuilder.WithClass(extensionClassBuilder);
foreach(var classDeclarationSyntax in classes)
{
if (classDeclarationSyntax is null)
{
continue;
}
if (GetParentOfType<BaseNamespaceDeclarationSyntax>(classDeclarationSyntax) is BaseNamespaceDeclarationSyntax baseNamespaceDeclarationSyntax)
{
routeUsings.Add(baseNamespaceDeclarationSyntax.Name.ToString());
}
var attributes = classDeclarationSyntax.AttributeLists
.SelectMany(l => l.Attributes)
.OfType<AttributeSyntax>().ToList();
if (attributes.FirstOrDefault(a => a.Name.ToString() == Constants.GetAttributeName || a.Name.ToString() == Constants.GetAttributeShortName) is AttributeSyntax getAttributeSyntax)
{
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.IEndpointRouteBuilderTypeName, $"MapGet{classDeclarationSyntax.Identifier}")
.WithThisParameter(Constants.IEndpointRouteBuilderTypeName, Constants.BuilderParameterName)
.WithModifiers($"{Constants.Public} {Constants.Static}");
var pattern = getAttributeSyntax.ArgumentList?.Arguments.OfType<AttributeArgumentSyntax>().FirstOrDefault(a => a.NameEquals?.Name.Identifier.Text == Constants.PatternPropertyName)?.Expression.ToString().Trim('"');
if (pattern is null)
{
continue;
}
methodBuilder.WithBody(@$"
builder.MapGet(""{pattern}"", async (HttpContext context, {classDeclarationSyntax.Identifier} route) =>
{{
var request = await route.PreProcess(context, context.RequestAborted);
var response = await route.HandleRequest(request, context.RequestAborted);
return response.GetResult();
}});
return builder;");
extensionClassBuilder.WithMethod(methodBuilder);
}
else if (attributes.FirstOrDefault(a => a.Name.ToString() == Constants.PostAttributeName || a.Name.ToString() == Constants.PostAttributeShortName) is AttributeSyntax postAttributeSyntax)
{
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.IEndpointRouteBuilderTypeName, $"MapPost{classDeclarationSyntax.Identifier}")
.WithThisParameter(Constants.IEndpointRouteBuilderTypeName, Constants.BuilderParameterName)
.WithModifiers($"{Constants.Public} {Constants.Static}");
var pattern = postAttributeSyntax.ArgumentList?.Arguments.OfType<AttributeArgumentSyntax>().FirstOrDefault(a => a.NameEquals?.Name.Identifier.Text == Constants.PatternPropertyName)?.Expression.ToString().Trim('"');
if (pattern is null)
{
continue;
}
methodBuilder.WithBody(@$"
builder.MapPost(""{pattern}"", async (HttpContext context, {classDeclarationSyntax.Identifier} route) =>
{{
var request = await route.PreProcess(context, context.RequestAborted);
var response = await route.HandleRequest(request, context.RequestAborted);
return response.GetResult();
}});
return builder;");
extensionClassBuilder.WithMethod(methodBuilder);
}
else if (attributes.FirstOrDefault(a => a.Name.ToString() == Constants.PutAttributeName || a.Name.ToString() == Constants.PutAttributeShortName) is AttributeSyntax putAttributeSyntax)
{
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.IEndpointRouteBuilderTypeName, $"MapPut{classDeclarationSyntax.Identifier}")
.WithThisParameter(Constants.IEndpointRouteBuilderTypeName, Constants.BuilderParameterName)
.WithModifiers($"{Constants.Public} {Constants.Static}");
var pattern = putAttributeSyntax.ArgumentList?.Arguments.OfType<AttributeArgumentSyntax>().FirstOrDefault(a => a.NameEquals?.Name.Identifier.Text == Constants.PatternPropertyName)?.Expression.ToString().Trim('"');
if (pattern is null)
{
continue;
}
methodBuilder.WithBody(@$"
builder.MapPut(""{pattern}"", async (HttpContext context, {classDeclarationSyntax.Identifier} route) =>
{{
var request = await route.PreProcess(context, context.RequestAborted);
var response = await route.HandleRequest(request, context.RequestAborted);
return response.GetResult();
}});
return builder;");
extensionClassBuilder.WithMethod(methodBuilder);
}
else if (attributes.FirstOrDefault(a => a.Name.ToString() == Constants.DeleteAttributeName || a.Name.ToString() == Constants.DeleteAttributeShortName) is AttributeSyntax deleteAttributeSyntax)
{
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.IEndpointRouteBuilderTypeName, $"MapDelete{classDeclarationSyntax.Identifier}")
.WithThisParameter(Constants.IEndpointRouteBuilderTypeName, Constants.BuilderParameterName)
.WithModifiers($"{Constants.Public} {Constants.Static}");
var pattern = deleteAttributeSyntax.ArgumentList?.Arguments.OfType<AttributeArgumentSyntax>().FirstOrDefault(a => a.NameEquals?.Name.Identifier.Text == Constants.PatternPropertyName)?.Expression.ToString().Trim('"');
if (pattern is null)
{
continue;
}
methodBuilder.WithBody(@$"
builder.MapDelete(""{pattern}"", async (HttpContext context, {classDeclarationSyntax.Identifier} route) =>
{{
var request = await route.PreProcess(context, context.RequestAborted);
var response = await route.HandleRequest(request, context.RequestAborted);
return response.GetResult();
}});
return builder;");
extensionClassBuilder.WithMethod(methodBuilder);
}
}
foreach(var classUsing in routeUsings)
{
builder.WithUsing(classUsing);
}
var fileSource = builder.Build().ToFullString();
sourceProductionContext.AddSource($"{Constants.EndpointRouteBuilderExtensionsName}.g", fileSource);
}
private static T? GetParentOfType<T>(SyntaxNode syntaxNode)
{
if (syntaxNode.Parent is null)
{
return default;
}
if (syntaxNode.Parent is T parentNode)
{
return parentNode;
}
return GetParentOfType<T>(syntaxNode.Parent);
}
}
#nullable disable
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.8</Version>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Alexandru Macocian</Authors>
<Description>Source generators extensions for Net.Sdk.Web.</Description>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<IsRoslynAnalyzer>true</IsRoslynAnalyzer>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.0" PrivateAssets="all" />
<PackageReference Include="Sybil" Version="0.8.3" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<!-- Package the generator in the analyzer directory of the nuget package -->
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\Sybil.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
@@ -0,0 +1,121 @@
using Microsoft.CodeAnalysis;
using Sybil;
namespace Net.Sdk.Web.Extensions.SourceGenerators;
[Generator(LanguageNames.CSharp)]
#nullable enable
public class ResponseGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(context =>
{
GenerateResponse(context);
});
}
private static void GenerateResponse(IncrementalGeneratorPostInitializationContext context)
{
var responseBaseClassBuilder = SyntaxBuilder.CreateCompilationUnit()
.WithUsing(Constants.UsingMicrosoftAspNetCoreHttp)
.WithNamespace(SyntaxBuilder.CreateNamespace(Constants.Namespace)
.WithClass(SyntaxBuilder.CreateClass(Constants.ResponseTypeName)
.WithModifiers($"{Constants.Public} {Constants.Abstract}")
.WithMethod(SyntaxBuilder.CreateMethod(Constants.IResultTypeName, Constants.GetResult)
.WithModifier(Constants.Public)
.WithBody(
@"return this switch
{
SuccessResponse success => Results.StatusCode(success.StatusCode),
FailureResponse failure => Results.Problem(
detail: string.IsNullOrWhiteSpace(failure.ErrorMessage) is false ? failure.ErrorMessage : default,
statusCode: failure.StatusCode),
ContentResponse content => Results.Content(content.ContentString, content.ContentType),
JsonResponse json => Results.Ok(json.Value),
_ => throw new InvalidOperationException($""Unexpected response type {this.GetType().Name}"")
};"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.SuccessResponseTypeName, Constants.OkSuccessResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithExpression("new() { StatusCode = 200 };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.SuccessResponseTypeName, Constants.CreatedSuccessResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithExpression("new() { StatusCode = 201 };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.ContentResponseTypeName, Constants.ContentContentResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.StringType, Constants.ContentParameterName)
.WithParameter($"{Constants.StringType}?", Constants.ContentTypeParameterName, Constants.Default)
.WithExpression("new() { ContentString = content, ContentType = contentType };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.JsonResponseTypeName, Constants.JsonJsonResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.ObjectType, Constants.ValueParameterName)
.WithExpression("new() { Value = value };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.ContentResponseTypeName, Constants.PageContentResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.StringType, Constants.ContentParameterName)
.WithExpression(@"new() { ContentString = content, ContentType = ""text/html"" };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.FailureResponseTypeName, Constants.BadRequestFailureResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.StringType, Constants.ErrorMessageParameterName)
.WithExpression(@"new() { StatusCode = 400, ErrorMessage = errorMessage };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.FailureResponseTypeName, Constants.UnauthorizedFailureResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.StringType, Constants.ErrorMessageParameterName)
.WithExpression(@"new() { StatusCode = 401, ErrorMessage = errorMessage };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.FailureResponseTypeName, Constants.ForbiddenFailureResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.StringType, Constants.ErrorMessageParameterName)
.WithExpression(@"new() { StatusCode = 403, ErrorMessage = errorMessage };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.FailureResponseTypeName, Constants.NotFoundFailureResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.StringType, Constants.ErrorMessageParameterName)
.WithExpression(@"new() { StatusCode = 404, ErrorMessage = errorMessage };"))
.WithMethod(SyntaxBuilder.CreateMethod(Constants.FailureResponseTypeName, Constants.ServerErrorFailureResponse)
.WithModifiers($"{Constants.Public} {Constants.Static}")
.WithParameter(Constants.StringType, Constants.ErrorMessageParameterName)
.WithExpression(@"new() { StatusCode = 500, ErrorMessage = errorMessage };")))
.WithClass(SyntaxBuilder.CreateClass(Constants.SuccessResponseTypeName)
.WithModifiers($"{Constants.Public} {Constants.Sealed}")
.WithBaseClass(Constants.ResponseTypeName)
.WithProperty(SyntaxBuilder.CreateProperty(Constants.IntType, Constants.StatusCodePropertyName)
.WithModifier(Constants.Public)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter())))
.WithClass(SyntaxBuilder.CreateClass(Constants.FailureResponseTypeName)
.WithModifiers($"{Constants.Public} {Constants.Sealed}")
.WithBaseClass(Constants.ResponseTypeName)
.WithProperty(SyntaxBuilder.CreateProperty(Constants.IntType, Constants.StatusCodePropertyName)
.WithModifier(Constants.Public)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter()))
.WithProperty(SyntaxBuilder.CreateProperty($"{Constants.StringType}?", Constants.ErrorMessagePropertyName)
.WithModifier(Constants.Public)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter())))
.WithClass(SyntaxBuilder.CreateClass(Constants.ContentResponseTypeName)
.WithModifiers($"{Constants.Public} {Constants.Sealed}")
.WithBaseClass(Constants.ResponseTypeName)
.WithProperty(SyntaxBuilder.CreateProperty($"{Constants.StringType}?", Constants.ContentStringPropertyName)
.WithModifier(Constants.Public)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter()))
.WithProperty(SyntaxBuilder.CreateProperty($"{Constants.StringType}?", Constants.ContentTypePropertyName)
.WithModifier(Constants.Public)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter())))
.WithClass(SyntaxBuilder.CreateClass(Constants.JsonResponseTypeName)
.WithModifiers($"{Constants.Public} {Constants.Sealed}")
.WithBaseClass(Constants.ResponseTypeName)
.WithProperty(SyntaxBuilder.CreateProperty($"{Constants.ObjectType}?", Constants.ValuePropertyName)
.WithModifier(Constants.Public)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter())))
);
var syntax = responseBaseClassBuilder.Build();
var source = syntax.ToFullString();
context.AddSource($"Response.g", $"#nullable enable\n{source}\n#nullable disable\n");
}
}
#nullable disable
@@ -0,0 +1,47 @@
using System;
using Microsoft.CodeAnalysis;
using Sybil;
namespace Net.Sdk.Web.Extensions.SourceGenerators;
[Generator(LanguageNames.CSharp)]
#nullable enable
public class RouteAttributeGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(context =>
{
GenerateMapAttribute(context, Constants.GetAttributeName);
GenerateMapAttribute(context, Constants.PostAttributeName);
GenerateMapAttribute(context, Constants.PutAttributeName);
GenerateMapAttribute(context, Constants.DeleteAttributeName);
});
}
private static void GenerateMapAttribute(IncrementalGeneratorPostInitializationContext context, string attributeName)
{
var builder = SyntaxBuilder.CreateCompilationUnit()
.WithNamespace(
SyntaxBuilder.CreateNamespace(Constants.Namespace)
.WithClass(SyntaxBuilder.CreateClass(attributeName)
.WithModifier(Constants.Public)
.WithConstructor(SyntaxBuilder.CreateConstructor(attributeName)
.WithModifier(Constants.Public))
.WithAttribute(SyntaxBuilder.CreateAttribute("AttributeUsage")
.WithArgument(AttributeTargets.Class)
.WithArgument("Inherited", false)
.WithArgument("AllowMultiple", false))
.WithBaseClass(nameof(Attribute))
.WithProperty(SyntaxBuilder.CreateProperty(Constants.StringType, Constants.PatternPropertyName)
.WithModifier(Constants.Public)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter()))));
var syntax = builder.Build();
var source = syntax.ToFullString();
context.AddSource($"{attributeName}.g", source);
}
}
#nullable disable
@@ -0,0 +1,43 @@
using Microsoft.CodeAnalysis;
using Sybil;
namespace Net.Sdk.Web.Extensions.SourceGenerators;
[Generator(LanguageNames.CSharp)]
#nullable enable
public class RouteInterfaceGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(context =>
{
GenerateInterface(context);
});
}
private static void GenerateInterface(IncrementalGeneratorPostInitializationContext context)
{
var builder = SyntaxBuilder.CreateCompilationUnit()
.WithNamespace(SyntaxBuilder.CreateNamespace(Constants.Namespace)
.WithInterface(SyntaxBuilder.CreateInterface(Constants.RouteInterface)
.WithTypeParameter(SyntaxBuilder.CreateTypeParameter(Constants.RouteTypeParameter))
.WithTypeParameterConstraint(SyntaxBuilder.CreateTypeParameterConstraint(Constants.RouteTypeParameter)
.WithClass())
.WithMethod(SyntaxBuilder.CreateMethod(Constants.PreProcessRequestReturnType, Constants.PreProcessRequestMethodName)
.WithModifier(Constants.Public)
.WithParameter(Constants.HttpContextTypeName, Constants.HttpContextParameterName)
.WithParameter(Constants.CancellationTokenTypeName, Constants.CancellationTokenParameterName)
.WithNoBody())
.WithMethod(SyntaxBuilder.CreateMethod(Constants.HandleRequestReturnType, Constants.HandleRequestMethodName)
.WithModifier(Constants.Public)
.WithParameter($"{Constants.RouteTypeParameter}?", Constants.RequestParameterName)
.WithParameter(Constants.CancellationTokenTypeName, Constants.CancellationTokenParameterName)
.WithNoBody())));
var syntax = builder.Build();
var source = syntax.ToFullString();
context.AddSource($"{Constants.RouteFileName}.g", source);
}
}
#nullable disable