Skip to content

Commit

Permalink
Reorganize Code (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
viceroypenguin authored Mar 21, 2024
1 parent be902e9 commit 40a4248
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 152 deletions.
24 changes: 24 additions & 0 deletions src/Immediate.Apis.Generators/ImmediateApisGenerator.Models.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Immediate.Apis.Generators;

public sealed partial class ImmediateApisGenerator
{
private sealed record Method
{
public required string Route { get; init; }
public required string ClassName { get; init; }
public required string MethodName { get; init; }
public required string ParameterType { get; init; }
public required bool AllowAnonymous { get; init; }
public required bool Authorize { get; init; }
public required string? AuthorizePolicy { get; init; }
}

private static readonly string[] s_methodAttributes =
[
"Immediate.Apis.Shared.MapGetAttribute",
"Immediate.Apis.Shared.MapPostAttribute",
"Immediate.Apis.Shared.MapPutAttribute",
"Immediate.Apis.Shared.MapPatchAttribute",
"Immediate.Apis.Shared.MapDeleteAttribute",
];
}
31 changes: 31 additions & 0 deletions src/Immediate.Apis.Generators/ImmediateApisGenerator.Render.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;

namespace Immediate.Apis.Generators;

public sealed partial class ImmediateApisGenerator
{
private static void RenderMethods(
SourceProductionContext context,
ImmutableArray<Method> methods,
string assemblyName
)
{
if (methods.Length == 0)
return;

var token = context.CancellationToken;

var template = Utility.GetTemplate("Routes");
token.ThrowIfCancellationRequested();

var source = template.Render(new
{
Assembly = assemblyName,
Methods = methods,
});

token.ThrowIfCancellationRequested();
context.AddSource("RoutesBuilder.g.cs", source);
}
}
110 changes: 110 additions & 0 deletions src/Immediate.Apis.Generators/ImmediateApisGenerator.Transform.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using Microsoft.CodeAnalysis;

namespace Immediate.Apis.Generators;

public sealed partial class ImmediateApisGenerator
{
private static Method? TransformMethod(
GeneratorAttributeSyntaxContext context,
CancellationToken token
)
{
token.ThrowIfCancellationRequested();

var symbol = (INamedTypeSymbol)context.TargetSymbol;
var displayName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

token.ThrowIfCancellationRequested();

if (symbol.ContainingType is not null)
return null;

token.ThrowIfCancellationRequested();

if (symbol
.GetMembers()
.OfType<IMethodSymbol>()
.Where(m => m.IsStatic)
.Where(m =>
m.Name.Equals("Handle", StringComparison.Ordinal)
|| m.Name.Equals("HandleAsync", StringComparison.Ordinal)
)
.ToList() is not [var handleMethod])
{
return null;
}

// must have request type and cancellation token
if (handleMethod.Parameters.Length < 2)
return null;

token.ThrowIfCancellationRequested();

var requestType = handleMethod.Parameters[0].Type
.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

token.ThrowIfCancellationRequested();

var attributeNames = symbol.GetAttributes()
.Select(a => a.AttributeClass?.ToString() ?? "")
.ToList();

foreach (var methodName in s_methodAttributes)
{
var methodIndex = attributeNames.IndexOf(methodName);
if (methodIndex < 0)
continue;

token.ThrowIfCancellationRequested();

var attribute = symbol.GetAttributes()[methodIndex];
var method = attribute.AttributeClass!.Name[..^9];
var route = (string?)attribute.ConstructorArguments.FirstOrDefault().Value;

if (route == null)
return null;

token.ThrowIfCancellationRequested();

var allowAnonymous = attributeNames.Contains("Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute");

var authorizeIndex = attributeNames.IndexOf("Microsoft.AspNetCore.Authorization.AuthorizeAttribute");
var authorize = authorizeIndex >= 0;
var authorizePolicy = string.Empty;

if (authorize)
{
var authorizeAttribute = symbol.GetAttributes()[authorizeIndex];
if (authorizeAttribute.ConstructorArguments.Length > 0)
{
authorizePolicy = (string)authorizeAttribute.ConstructorArguments[0].Value!;
}
else if (authorizeAttribute.NamedArguments.Length > 0)
{
foreach (var argument in authorizeAttribute.NamedArguments)
{
if (argument.Key != "Policy")
return null;

authorizePolicy = (string)argument.Value.Value!;
}
}
}

token.ThrowIfCancellationRequested();

return new()
{
Route = route,
ClassName = displayName,
MethodName = method,
ParameterType = requestType,
AllowAnonymous = allowAnonymous,
Authorize = authorize,
AuthorizePolicy = authorizePolicy
};
}

return null;
}
}
153 changes: 2 additions & 151 deletions src/Immediate.Apis.Generators/ImmediateApisGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;

namespace Immediate.Apis.Generators;

[Generator]
public sealed class ImmediateApisGenerator : IIncrementalGenerator
public sealed partial class ImmediateApisGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var methods = context.SyntaxProvider
.ForAttributeWithMetadataName(
$"Immediate.Handlers.Shared.HandlerAttribute",
"Immediate.Handlers.Shared.HandlerAttribute",
(_, _) => true,
TransformMethod
)
Expand All @@ -29,152 +28,4 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
(spc, m) => RenderMethods(spc, m.Left!, m.Right)
);
}

private sealed record Method
{
public required string Route { get; init; }
public required string ClassName { get; init; }
public required string MethodName { get; init; }
public required string ParameterType { get; init; }
public required bool AllowAnonymous { get; init; }
public required bool Authorize { get; init; }
public required string? AuthorizePolicy { get; init; }
}

private static readonly string[] s_methodAttributes =
[
"Immediate.Apis.Shared.MapGetAttribute",
"Immediate.Apis.Shared.MapPostAttribute",
"Immediate.Apis.Shared.MapPutAttribute",
"Immediate.Apis.Shared.MapPatchAttribute",
"Immediate.Apis.Shared.MapDeleteAttribute",
];

private static Method? TransformMethod(
GeneratorAttributeSyntaxContext context,
CancellationToken token
)
{
token.ThrowIfCancellationRequested();

var symbol = (INamedTypeSymbol)context.TargetSymbol;
var displayName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

token.ThrowIfCancellationRequested();

if (symbol.ContainingType is not null)
return null;

token.ThrowIfCancellationRequested();

if (symbol
.GetMembers()
.OfType<IMethodSymbol>()
.Where(m => m.IsStatic)
.Where(m =>
m.Name.Equals("Handle", StringComparison.Ordinal)
|| m.Name.Equals("HandleAsync", StringComparison.Ordinal)
)
.ToList() is not [var handleMethod])
{
return null;
}

// must have request type and cancellation token
if (handleMethod.Parameters.Length < 2)
return null;

token.ThrowIfCancellationRequested();

var requestType = handleMethod.Parameters[0].Type
.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

token.ThrowIfCancellationRequested();

var attributeNames = symbol.GetAttributes()
.Select(a => a.AttributeClass?.ToString() ?? "")
.ToList();

foreach (var methodName in s_methodAttributes)
{
var methodIndex = attributeNames.IndexOf(methodName);
if (methodIndex < 0)
continue;

token.ThrowIfCancellationRequested();

var attribute = symbol.GetAttributes()[methodIndex];
var method = attribute.AttributeClass!.Name[..^9];
var route = (string?)attribute.ConstructorArguments.FirstOrDefault().Value;

if (route == null)
return null;

token.ThrowIfCancellationRequested();

var allowAnonymous = attributeNames.Contains("Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute");

var authorizeIndex = attributeNames.IndexOf("Microsoft.AspNetCore.Authorization.AuthorizeAttribute");
var authorize = authorizeIndex >= 0;
var authorizePolicy = string.Empty;

if (authorize)
{
var authorizeAttribute = symbol.GetAttributes()[authorizeIndex];
if (authorizeAttribute.ConstructorArguments.Length > 0)
{
authorizePolicy = (string)authorizeAttribute.ConstructorArguments[0].Value!;
}
else if (authorizeAttribute.NamedArguments.Length > 0)
{
foreach (var argument in authorizeAttribute.NamedArguments)
{
if (argument.Key != "Policy")
return null;

authorizePolicy = (string)argument.Value.Value!;
}
}
}

token.ThrowIfCancellationRequested();

return new()
{
Route = route,
ClassName = displayName,
MethodName = method,
ParameterType = requestType,
AllowAnonymous = allowAnonymous,
Authorize = authorize,
AuthorizePolicy = authorizePolicy
};
}

return null;
}

private static void RenderMethods(
SourceProductionContext context,
ImmutableArray<Method> methods,
string assemblyName
)
{
if (methods.Length == 0)
return;

var token = context.CancellationToken;

var template = Utility.GetTemplate("Routes");
token.ThrowIfCancellationRequested();

var source = template.Render(new
{
Assembly = assemblyName,
Methods = methods,
});

token.ThrowIfCancellationRequested();
context.AddSource("RoutesBuilder.g.cs", source);
}
}
2 changes: 1 addition & 1 deletion src/Immediate.Apis.Shared/Immediate.Apis.Shared.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<NoWarn>$(NoWarn);CA1716</NoWarn>
</PropertyGroup>
Expand Down

0 comments on commit 40a4248

Please sign in to comment.