Skip to content

Commit 5369c5d

Browse files
committed
SourceGenerator improvement
- Seperating out the 'writer' - Adding tests - Upgrade 'futurum' nugets
1 parent 6e578d9 commit 5369c5d

27 files changed

+409
-170
lines changed

Futurum.WebApiEndpoint.Micro.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Futurum.WebApiEndpoint.Micr
3030
EndProject
3131
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Futurum.WebApiEndpoint.Micro.Sample.Addition", "sample\Futurum.WebApiEndpoint.Micro.Sample.Addition\Futurum.WebApiEndpoint.Micro.Sample.Addition.csproj", "{F4BA916B-A0A8-485E-8ACC-D5698B4D3632}"
3232
EndProject
33+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Futurum.WebApiEndpoint.Micro.Generator.Writer.Tests", "test\Futurum.WebApiEndpoint.Micro.Generator.Writer.Tests\Futurum.WebApiEndpoint.Micro.Generator.Writer.Tests.csproj", "{5AD57853-5EEB-4960-98B5-6AC4D75CF7ED}"
34+
EndProject
35+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Futurum.WebApiEndpoint.Micro.Generator.Tests", "test\Futurum.WebApiEndpoint.Micro.Generator.Tests\Futurum.WebApiEndpoint.Micro.Generator.Tests.csproj", "{C4BB247F-941F-41AF-B422-083E203A46FE}"
36+
EndProject
3337
Global
3438
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3539
Debug|Any CPU = Debug|Any CPU
@@ -60,11 +64,21 @@ Global
6064
{F4BA916B-A0A8-485E-8ACC-D5698B4D3632}.Debug|Any CPU.Build.0 = Debug|Any CPU
6165
{F4BA916B-A0A8-485E-8ACC-D5698B4D3632}.Release|Any CPU.ActiveCfg = Release|Any CPU
6266
{F4BA916B-A0A8-485E-8ACC-D5698B4D3632}.Release|Any CPU.Build.0 = Release|Any CPU
67+
{5AD57853-5EEB-4960-98B5-6AC4D75CF7ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68+
{5AD57853-5EEB-4960-98B5-6AC4D75CF7ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
69+
{5AD57853-5EEB-4960-98B5-6AC4D75CF7ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
70+
{5AD57853-5EEB-4960-98B5-6AC4D75CF7ED}.Release|Any CPU.Build.0 = Release|Any CPU
71+
{C4BB247F-941F-41AF-B422-083E203A46FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
72+
{C4BB247F-941F-41AF-B422-083E203A46FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
73+
{C4BB247F-941F-41AF-B422-083E203A46FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
74+
{C4BB247F-941F-41AF-B422-083E203A46FE}.Release|Any CPU.Build.0 = Release|Any CPU
6375
EndGlobalSection
6476
GlobalSection(NestedProjects) = preSolution
6577
{5A199733-F1B8-48E6-8591-776E2EC7E81E} = {3D057B76-ED4D-45EF-AC8B-7A4660ED32C2}
6678
{E198A79A-EAFA-40E5-B564-0B17EC5CA69F} = {759534C0-6CAD-4645-932D-8EFEE7A3FE99}
6779
{F73126B2-18F2-4848-B58A-D41592D7DAA6} = {759534C0-6CAD-4645-932D-8EFEE7A3FE99}
6880
{F4BA916B-A0A8-485E-8ACC-D5698B4D3632} = {3D057B76-ED4D-45EF-AC8B-7A4660ED32C2}
81+
{5AD57853-5EEB-4960-98B5-6AC4D75CF7ED} = {759534C0-6CAD-4645-932D-8EFEE7A3FE99}
82+
{C4BB247F-941F-41AF-B422-083E203A46FE} = {759534C0-6CAD-4645-932D-8EFEE7A3FE99}
6983
EndGlobalSection
7084
EndGlobal

Futurum.WebApiEndpoint.Micro.sln.DotSettings

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_EXPR_PROPERTY_ON_SINGLE_LINE/@EntryValue">NEVER</s:String>
2121
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">200</s:Int64>
2222
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
23+
<s:Boolean x:Key="/Default/Environment/Filtering/ExcludeCoverageFilters/=Futurum_002EWebApiEndpoint_002EMicro_002EEndToEndTests_003B_002A_003B_002A_003B_002A/@EntryIndexedValue">True</s:Boolean>
24+
<s:Boolean x:Key="/Default/Environment/Filtering/ExcludeCoverageFilters/=Futurum_002EWebApiEndpoint_002EMicro_002EGenerator_002EWriter_002ETests_003B_002A_003B_002A_003B_002A/@EntryIndexedValue">True</s:Boolean>
25+
<s:Boolean x:Key="/Default/Environment/Filtering/ExcludeCoverageFilters/=Futurum_002EWebApiEndpoint_002EMicro_002ETests_003B_002A_003B_002A_003B_002A/@EntryIndexedValue">True</s:Boolean>
2326
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
2427
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
2528
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>

sample/Futurum.WebApiEndpoint.Micro.Sample/ApplicationModule.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public ApplicationModule(IConfiguration configuration)
1515

1616
public void Load(IServiceCollection services)
1717
{
18-
services.RegisterModule<BlogModule>();
19-
services.RegisterModule(new TodoModule(_configuration));
18+
services.AddModule<BlogModule>();
19+
services.AddModule(new TodoModule(_configuration));
2020
}
2121
}

sample/Futurum.WebApiEndpoint.Micro.Sample/Program.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
using Futurum.Microsoft.Extensions.DependencyInjection;
22
using Futurum.WebApiEndpoint.Micro;
33
using Futurum.WebApiEndpoint.Micro.Sample;
4-
using Futurum.WebApiEndpoint.Micro.Sample.Addition;
54

65
using Microsoft.OpenApi.Models;
76

87
var builder = WebApplication.CreateBuilder(args);
98
builder.Services.AddProblemDetails();
109

11-
builder.Services.RegisterModule(new ApplicationModule(builder.Configuration));
10+
builder.Services.AddModule(new ApplicationModule(builder.Configuration));
1211

1312
builder.AddAuthentication();
1413

sample/Futurum.WebApiEndpoint.Micro.Sample/Todo/TodoStartable.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,23 @@ public TodoStartable(IServiceProvider serviceProvider,
1919
_configurationService = configurationService;
2020
}
2121

22-
public void Start()
22+
public async Task StartAsync()
2323
{
2424
var connectionString = _configurationService.GetConnectionString();
2525

2626
if (Environment.GetEnvironmentVariable("SUPPRESS_DB_INIT") != "true")
2727
{
2828
_logger.LogInformation("Ensuring database exists at connection string '{connectionString}'", connectionString);
2929

30-
using var db = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService<SqliteConnection>();
30+
await using var db = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService<SqliteConnection>();
3131
var sql = $"""
3232
CREATE TABLE IF NOT EXISTS Todos (
3333
{nameof(Todo.Id)} INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
3434
{nameof(Todo.Title)} TEXT NOT NULL,
3535
{nameof(Todo.IsComplete)} INTEGER DEFAULT 0 NOT NULL CHECK({nameof(Todo.IsComplete)} IN (0, 1))
3636
);
3737
""";
38-
db.ExecuteAsync(sql).Wait();
38+
await db.ExecuteAsync(sql);
3939
}
4040
else
4141
{

src/Futurum.WebApiEndpoint.Micro.Generator/FluentValidatorDatum.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public bool Equals(FluentValidatorDatum? other)
2626
}
2727

2828
public override bool Equals(object? obj) =>
29-
obj is FluentValidatorDatum fluentValidatorData && Equals(fluentValidatorData);
29+
obj is FluentValidatorDatum fluentValidatorDatum && Equals(fluentValidatorDatum);
3030

3131
public override int GetHashCode() =>
3232
HashCode.Combine(InterfaceType, ImplementationType);

src/Futurum.WebApiEndpoint.Micro.Generator/FluentValidatorSourceGenerator.cs

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
using System.Text;
33
using System.Text.RegularExpressions;
44

5-
using Futurum.WebApiEndpoint.Micro.Generator.Core;
6-
75
using Microsoft.CodeAnalysis;
86
using Microsoft.CodeAnalysis.CSharp;
97
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -29,9 +27,9 @@ public static bool SemanticPredicate(SyntaxNode node, CancellationToken _) =>
2927
.FirstOrDefault(t => t.ContainingNamespace.MetadataName == "FluentValidation" && t.MetadataName == "IValidator`1");
3028
if (interfaceNamedTypeSymbol != null)
3129
{
32-
var fluentValidatorData = new FluentValidatorDatum(interfaceNamedTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
33-
classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
34-
return new FluentValidatorContext(fluentValidatorData: new[] { fluentValidatorData });
30+
var fluentValidatorDatum = new FluentValidatorDatum(interfaceNamedTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
31+
classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
32+
return new FluentValidatorContext(fluentValidatorData: new[] { fluentValidatorDatum });
3533
}
3634
}
3735

@@ -50,41 +48,7 @@ public static void ExecuteGeneration(SourceProductionContext context, ImmutableA
5048
.Where(registrationData => registrationData is not null)
5149
.ToArray();
5250

53-
var codeBuilder = new IndentedStringBuilder();
54-
55-
codeBuilder
56-
.AppendLine("// <auto-generated />")
57-
.AppendLine("#nullable enable")
58-
.AppendLine();
59-
60-
codeBuilder
61-
.AppendLine($"namespace {assemblyName}")
62-
.AppendLine("{")
63-
.IncrementIndent();
64-
65-
codeBuilder
66-
.AppendLine($"public static partial class {methodName}Extensions")
67-
.AppendLine("{")
68-
.IncrementIndent()
69-
.Append("public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection")
70-
.Append(" RegisterFluentValidators")
71-
.AppendLine("(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection)")
72-
.AppendLine("{")
73-
.IncrementIndent();
74-
75-
foreach (var fluentValidatorDatum in fluentValidatorData)
76-
{
77-
codeBuilder.AppendLine($"serviceCollection.AddSingleton(typeof({fluentValidatorDatum.InterfaceType}), typeof({fluentValidatorDatum.ImplementationType}));");
78-
}
79-
80-
codeBuilder
81-
.AppendLine("return serviceCollection;")
82-
.DecrementIndent()
83-
.AppendLine("}") // method
84-
.DecrementIndent()
85-
.AppendLine("}") // class
86-
.DecrementIndent()
87-
.AppendLine("}"); // namespace
51+
var codeBuilder = FluentValidatorWriter.Write(methodName, fluentValidatorData);
8852

8953
context.AddSource("FluentValidators.g.cs", SourceText.From(codeBuilder.ToString(), Encoding.UTF8));
9054
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Futurum.WebApiEndpoint.Micro.Generator.Core;
2+
3+
namespace Futurum.WebApiEndpoint.Micro.Generator;
4+
5+
public static class FluentValidatorWriter
6+
{
7+
public static string Write(string methodName, IEnumerable<FluentValidatorDatum> fluentValidatorData) =>
8+
WrapperSourceGeneratorWriter.Write(methodName, "RegisterFluentValidators",
9+
codeBuilder => Write(codeBuilder, fluentValidatorData),
10+
true);
11+
12+
private static void Write(IndentedStringBuilder codeBuilder, IEnumerable<FluentValidatorDatum> fluentValidatorData)
13+
{
14+
foreach (var fluentValidatorDatum in fluentValidatorData)
15+
{
16+
codeBuilder.AppendLine($"serviceCollection.AddSingleton(typeof({fluentValidatorDatum.InterfaceType}), typeof({fluentValidatorDatum.ImplementationType}));");
17+
}
18+
}
19+
}
Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,63 @@
99
namespace Futurum.WebApiEndpoint.Micro.Generator;
1010

1111
[Generator]
12-
public class FuturumWebApiEndpointMicroGenerator : IIncrementalGenerator
12+
public class SourceGenerator : IIncrementalGenerator
1313
{
1414
public void Initialize(IncrementalGeneratorInitializationContext context)
15+
{
16+
var assemblyName = context.CompilationProvider
17+
.Select(static (c, _) => c.AssemblyName);
18+
19+
Generator(context, assemblyName);
20+
21+
WebApiEndpoint(context, assemblyName);
22+
23+
FluentValidator(context, assemblyName);
24+
}
25+
26+
private static void Generator(IncrementalGeneratorInitializationContext context, IncrementalValueProvider<string?> assemblyName)
27+
{
28+
context.RegisterSourceOutput(assemblyName,
29+
static (productionContext, assemblyName) => ExecuteGeneration(productionContext, assemblyName));
30+
31+
static void ExecuteGeneration(SourceProductionContext context, string assemblyName)
32+
{
33+
var methodName = Regex.Replace(assemblyName, "\\W", "");
34+
35+
var codeBuilder = SourceGeneratorWriter.Write(methodName);
36+
37+
context.AddSource("Generator.g.cs", SourceText.From(codeBuilder.ToString(), Encoding.UTF8));
38+
}
39+
}
40+
41+
private static void WebApiEndpoint(IncrementalGeneratorInitializationContext context, IncrementalValueProvider<string?> assemblyName)
1542
{
1643
var webApiEndpointData = context.SyntaxProvider
1744
.CreateSyntaxProvider(WebApiEndpointSourceGenerator.SemanticPredicate, WebApiEndpointSourceGenerator.SemanticTransform)
1845
.Where(node => node is not null);
1946

20-
var fluentValidatorData = context.SyntaxProvider
21-
.CreateSyntaxProvider(FluentValidatorSourceGenerator.SemanticPredicate, FluentValidatorSourceGenerator.SemanticTransform)
22-
.Where(node => node is not null);
23-
24-
// Emit the diagnostics, if needed
2547
var webApiEndpointDiagnostics = webApiEndpointData
2648
.Select(static (item, _) => item.Diagnostics)
2749
.Where(static item => item.Count > 0);
2850

2951
context.RegisterSourceOutput(webApiEndpointDiagnostics, ReportDiagnostic);
3052

31-
// Emit the diagnostics, if needed
53+
context.RegisterSourceOutput(webApiEndpointData.Collect().Combine(assemblyName),
54+
static (productionContext, source) => WebApiEndpointSourceGenerator.ExecuteGeneration(productionContext, source.Left, source.Right));
55+
}
56+
57+
private static void FluentValidator(IncrementalGeneratorInitializationContext context, IncrementalValueProvider<string?> assemblyName)
58+
{
59+
var fluentValidatorData = context.SyntaxProvider
60+
.CreateSyntaxProvider(FluentValidatorSourceGenerator.SemanticPredicate, FluentValidatorSourceGenerator.SemanticTransform)
61+
.Where(node => node is not null);
62+
3263
var fluentValidatorDiagnostics = fluentValidatorData
3364
.Select(static (item, _) => item.Diagnostics)
3465
.Where(static item => item.Count > 0);
3566

3667
context.RegisterSourceOutput(fluentValidatorDiagnostics, ReportDiagnostic);
3768

38-
// include config options
39-
var assemblyName = context.CompilationProvider
40-
.Select(static (c, _) => c.AssemblyName);
41-
42-
context.RegisterSourceOutput(assemblyName,
43-
static (productionContext, assemblyName) => ExecuteGeneration(productionContext, assemblyName));
44-
45-
context.RegisterSourceOutput(webApiEndpointData.Collect().Combine(assemblyName),
46-
static (productionContext, source) => WebApiEndpointSourceGenerator.ExecuteGeneration(productionContext, source.Left, source.Right));
47-
4869
context.RegisterSourceOutput(fluentValidatorData.Collect().Combine(assemblyName),
4970
static (productionContext, source) => FluentValidatorSourceGenerator.ExecuteGeneration(productionContext, source.Left, source.Right));
5071
}
@@ -54,13 +75,4 @@ private static void ReportDiagnostic(SourceProductionContext context, EquatableA
5475
foreach (var diagnostic in diagnostics)
5576
context.ReportDiagnostic(diagnostic);
5677
}
57-
58-
private static void ExecuteGeneration(SourceProductionContext context, string assemblyName)
59-
{
60-
var methodName = Regex.Replace(assemblyName, "\\W", "");
61-
62-
var codeBuilder = SourceGeneratorWriter.Write(assemblyName, methodName);
63-
64-
context.AddSource("Generator.g.cs", SourceText.From(codeBuilder.ToString(), Encoding.UTF8));
65-
}
6678
}

src/Futurum.WebApiEndpoint.Micro.Generator/SourceGeneratorWriter.cs

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,15 @@ namespace Futurum.WebApiEndpoint.Micro.Generator;
44

55
public static class SourceGeneratorWriter
66
{
7-
public static IndentedStringBuilder Write(string assemblyName, string methodName)
8-
{
9-
var codeBuilder = new IndentedStringBuilder();
10-
11-
codeBuilder
12-
.AppendLine("// <auto-generated />")
13-
.AppendLine("#nullable enable")
14-
.AppendLine();
15-
16-
codeBuilder
17-
.AppendLine($"namespace {assemblyName}")
18-
.AppendLine("{")
19-
.IncrementIndent();
20-
21-
codeBuilder
22-
.Append("[global::System.CodeDom.Compiler.GeneratedCode(\"")
23-
.Append(ThisAssembly.Project.AssemblyName)
24-
.Append("\", \"")
25-
.Append(ThisAssembly.Info.Version)
26-
.AppendLine("\")]");
27-
28-
codeBuilder
29-
.AppendLine("[global::System.Diagnostics.DebuggerNonUserCodeAttribute]")
30-
.AppendLine("[global::System.Diagnostics.DebuggerStepThroughAttribute]")
31-
.AppendLine($"public static partial class {methodName}Extensions")
32-
.AppendLine("{")
33-
.IncrementIndent()
34-
.Append("public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection")
35-
.Append(" Add")
36-
.Append("WebApiEndpointsFor")
37-
.Append(methodName)
38-
.AppendLine("(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection)")
39-
.AppendLine("{")
40-
.IncrementIndent();
7+
public static string Write(string methodName, bool skipVersion = false) =>
8+
WrapperSourceGeneratorWriter.Write(methodName, $"AddWebApiEndpointsFor{methodName}",
9+
Write,
10+
false,
11+
skipVersion);
4112

13+
private static void Write(IndentedStringBuilder codeBuilder)
14+
{
4215
codeBuilder.AppendLine("serviceCollection.RegisterWebApiEndpoints();");
4316
codeBuilder.AppendLine("serviceCollection.RegisterFluentValidators();");
44-
45-
codeBuilder
46-
.AppendLine("return serviceCollection;")
47-
.DecrementIndent()
48-
.AppendLine("}") // method
49-
.DecrementIndent()
50-
.AppendLine("}") // class
51-
.DecrementIndent()
52-
.AppendLine("}"); // namespace
53-
54-
return codeBuilder;
5517
}
5618
}

0 commit comments

Comments
 (0)