aboutsummaryrefslogtreecommitdiffstats
path: root/code/source-generator/EndpointGenerator.cs
diff options
context:
space:
mode:
Diffstat (limited to 'code/source-generator/EndpointGenerator.cs')
-rw-r--r--code/source-generator/EndpointGenerator.cs146
1 files changed, 146 insertions, 0 deletions
diff --git a/code/source-generator/EndpointGenerator.cs b/code/source-generator/EndpointGenerator.cs
new file mode 100644
index 0000000..989a1c5
--- /dev/null
+++ b/code/source-generator/EndpointGenerator.cs
@@ -0,0 +1,146 @@
+using System.Text.RegularExpressions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace I2R.Endpoints.Generator;
+
+[Generator]
+public class EndpointGenerator : ISourceGenerator
+{
+ private const string SourceGenereatedComment = "// Generated by I2R.Endpoints, probably smart to leave it be";
+
+ public void Initialize(GeneratorInitializationContext context) {
+ context.RegisterForSyntaxNotifications(() => new EndpointFinder());
+ }
+
+ public void Execute(GeneratorExecutionContext context) {
+ var endpointClasses = ((EndpointFinder) context.SyntaxReceiver)?.Endpoints;
+ if (endpointClasses == null) {
+ context.ReportDiagnostic(CreateDebugDiagnostic("no endpoints were found"));
+ return;
+ }
+
+ foreach (var info in endpointClasses.Select(GetEndpointInfoFromClass)) {
+ context.AddSource(info.GeneratedFileName, info.IsSynchronous ? GetSyncSource(info) : GetAsyncSource(info));
+ }
+ }
+
+ private static Diagnostic CreateDebugDiagnostic(string message) {
+ var descriptor = new DiagnosticDescriptor("debug", "Debug", message, "debug", DiagnosticSeverity.Warning, true);
+ return Diagnostic.Create(descriptor, null, "");
+ }
+
+ private class EndpointInfo
+ {
+ public string BaseEndpointClassName { get; set; }
+ public bool IsSynchronous { get; set; }
+ public string GeneratedFileName { get; set; }
+ }
+
+ public static bool IsSyncEndpoint(string name) {
+ return name.StartsWith("SyncEndpoint");
+ }
+
+ public static bool IsAyncEndpoint(string name) {
+ return name.StartsWith("AsyncEndpoint");
+ }
+
+ private static EndpointInfo GetEndpointInfoFromClass(ClassDeclarationSyntax classDeclarationSyntax) {
+ var baseType = classDeclarationSyntax.BaseList?.Types.FirstOrDefault();
+ if (baseType == default) {
+ return default;
+ }
+
+ var fullTypeName = baseType.Type.ToString();
+ var className = Regex.Match(fullTypeName, "(!?<).+?(?=>)").Value.Replace("<", "");
+ return new EndpointInfo() {
+ BaseEndpointClassName = className,
+ IsSynchronous = IsSyncEndpoint(baseType.ToString()),
+ GeneratedFileName = className + ".endpoints.g.cs"
+ };
+ }
+
+ private static string GetSyncSource(EndpointInfo info) {
+ return $@"
+{SourceGenereatedComment}
+namespace {typeof(EndpointGenerator).Namespace};
+public static partial class SyncEndpoint<T{info.BaseEndpointClassName}>
+{{
+ public static class Req<TRequest>
+ {{
+ public abstract class Res<TResponse> : {info.BaseEndpointClassName}
+ {{
+ public abstract TResponse Handle(
+ TRequest request
+ );
+ }}
+
+ public abstract class NoRes : {info.BaseEndpointClassName}
+ {{
+ public abstract void Handle(
+ TRequest request
+ );
+ }}
+ }}
+
+ public static class NoReq
+ {{
+ public abstract class Res<TResponse> : {info.BaseEndpointClassName}
+ {{
+ public abstract TResponse Handle();
+ }}
+
+ public abstract class NoRes : {info.BaseEndpointClassName}
+ {{
+ public abstract void Handle();
+ }}
+ }}
+}}
+";
+ }
+
+ private static string GetAsyncSource(EndpointInfo info) {
+ return $@"
+{SourceGenereatedComment}
+namespace {typeof(EndpointGenerator).Namespace};
+public static partial class AsyncEndpoint<T>
+{{
+ public static class Req<TRequest>
+ {{
+ public abstract class Res<TResponse> : {info.BaseEndpointClassName}
+ {{
+ public abstract Task<TResponse> HandleAsync(
+ TRequest request,
+ CancellationToken cancellationToken = default
+ );
+ }}
+
+ public abstract class NoRes : {info.BaseEndpointClassName}
+ {{
+ public abstract Task HandleAsync(
+ TRequest request,
+ CancellationToken cancellationToken = default
+ );
+ }}
+ }}
+
+ public static class NoReq
+ {{
+ public abstract class Res<TResponse> : {info.BaseEndpointClassName}
+ {{
+ public abstract Task<TResponse> HandleAsync(
+ CancellationToken cancellationToken = default
+ );
+ }}
+
+ public abstract class NoRes : {info.BaseEndpointClassName}
+ {{
+ public abstract Task HandleAsync(
+ CancellationToken cancellationToken = default
+ );
+ }}
+ }}
+}}
+";
+ }
+}