From c2bcc128a3198d0e575a3682a7b05f9cf6d5e976 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 14 Aug 2025 17:38:51 +0100 Subject: [PATCH] Rework plugin register genrerator. --- Aaru.Generators/PluginRegisterGenerator.cs | 461 ++++----------------- 1 file changed, 88 insertions(+), 373 deletions(-) diff --git a/Aaru.Generators/PluginRegisterGenerator.cs b/Aaru.Generators/PluginRegisterGenerator.cs index 1cb655e87..d14be89f3 100644 --- a/Aaru.Generators/PluginRegisterGenerator.cs +++ b/Aaru.Generators/PluginRegisterGenerator.cs @@ -1,63 +1,87 @@ +#nullable enable using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; namespace Aaru.Generators; [Generator] -public class PluginRegisterGenerator : ISourceGenerator +public sealed class PluginRegisterGenerator : IIncrementalGenerator { -#region ISourceGenerator Members - - /// - public void Initialize(GeneratorInitializationContext context) => - - // Nothing to do - context.RegisterForSyntaxNotifications(() => new PluginFinder()); - - /// - public void Execute(GeneratorExecutionContext context) + private static readonly Dictionary PluginInterfaces = new() { - /* - #if DEBUG - if(!Debugger.IsAttached) - { - Debugger.Launch(); - } - #endif - */ + ["IArchive"] = "RegisterArchivePlugins", + ["IChecksum"] = "RegisterChecksumPlugins", + ["IFilesystem"] = "RegisterFilesystemPlugins", + ["IFilter"] = "RegisterFilterPlugins", + ["IFloppyImage"] = "RegisterFloppyImagePlugins", + ["IMediaImage"] = "RegisterMediaImagePlugins", + ["IPartition"] = "RegisterPartitionPlugins", + ["IReadOnlyFilesystem"] = "RegisterReadOnlyFilesystemPlugins", + ["IWritableFloppyImage"] = "RegisterWritableFloppyImagePlugins", + ["IWritableImage"] = "RegisterWritableImagePlugins", + ["IByteAddressableImage"] = "RegisterByteAddressablePlugins", + ["IFluxImage"] = "RegisterFluxImagePlugins", + ["IWritableFluxImage"] = "RegisterWritableFluxImagePlugins" + }; - ClassDeclarationSyntax pluginRegister = ((PluginFinder)context.SyntaxReceiver)?.Register; +#region IIncrementalGenerator Members - if(pluginRegister == null) return; + public void Initialize(IncrementalGeneratorInitializationContext context) + { + IncrementalValueProvider> pluginClasses = context.SyntaxProvider + .CreateSyntaxProvider(static (node, _) => node is ClassDeclarationSyntax, + static (ctx, _) => GetPluginInfo(ctx)) + .Where(static info => info is not null) + .Collect(); - var @namespace = - (pluginRegister.Ancestors().FirstOrDefault(x => x is FileScopedNamespaceDeclarationSyntax) as - FileScopedNamespaceDeclarationSyntax)?.Name.ToString(); + context.RegisterSourceOutput(pluginClasses, (ctx, pluginInfos) => GeneratePluginRegister(ctx, pluginInfos!)); + } - @namespace ??= - (pluginRegister.Ancestors().FirstOrDefault(x => x is NamespaceDeclarationSyntax) as - NamespaceDeclarationSyntax)?.ToString(); +#endregion - string className = pluginRegister.Identifier.Text; + private static PluginInfo? GetPluginInfo(GeneratorSyntaxContext context) + { + if(context.Node is not ClassDeclarationSyntax classDecl) return null; - List archives = ((PluginFinder)context.SyntaxReceiver)?.Archives; - List checksums = ((PluginFinder)context.SyntaxReceiver)?.Checksums; - List fileSystems = ((PluginFinder)context.SyntaxReceiver)?.FileSystems; - List filters = ((PluginFinder)context.SyntaxReceiver)?.Filters; - List floppyImagePlugins = ((PluginFinder)context.SyntaxReceiver)?.FloppyImagePlugins; - List partitionPlugins = ((PluginFinder)context.SyntaxReceiver)?.PartitionPlugins; - List mediaImagePlugins = ((PluginFinder)context.SyntaxReceiver)?.MediaImagePlugins; - List readOnlyFileSystems = ((PluginFinder)context.SyntaxReceiver)?.ReadOnlyFileSystems; - List writableFloppyImagePlugins = ((PluginFinder)context.SyntaxReceiver)?.WritableFloppyImagePlugins; - List writableImagePlugins = ((PluginFinder)context.SyntaxReceiver)?.WritableImagePlugins; - List byteAddressableImagePlugins = ((PluginFinder)context.SyntaxReceiver)?.ByteAddressableImagePlugins; - List fluxImagePlugins = ((PluginFinder)context.SyntaxReceiver)?.FluxImagePlugins; - List writableFluxImagePlugins = ((PluginFinder)context.SyntaxReceiver)?.WritableFluxImagePlugins; + var info = new PluginInfo + { + ClassName = classDecl.Identifier.Text, + Namespace = GetNamespace(classDecl), + IsRegister = ImplementsInterface(classDecl, "IPluginRegister") + }; - StringBuilder sb = new(); + foreach(string? iface in PluginInterfaces.Keys) + { + if(ImplementsInterface(classDecl, iface)) info.Interfaces.Add(iface); + } + + if(info is { IsRegister: false, Interfaces.Count: 0 }) return null; + + return info; + } + + private static bool ImplementsInterface(ClassDeclarationSyntax classDecl, string interfaceName) + { + return classDecl.BaseList?.Types.Any(t => (t.Type as IdentifierNameSyntax)?.Identifier.ValueText == + interfaceName) == + true; + } + + private static string? GetNamespace(SyntaxNode node) => + node.Ancestors().OfType().FirstOrDefault()?.Name.ToString(); + + private static void GeneratePluginRegister(SourceProductionContext context, IReadOnlyList pluginInfos) + { + PluginInfo? registerClass = pluginInfos.FirstOrDefault(p => p.IsRegister); + + if(registerClass is null) return; + + var sb = new StringBuilder(); sb.AppendLine(""" // /*************************************************************************** @@ -97,357 +121,48 @@ public class PluginRegisterGenerator : ISourceGenerator // ****************************************************************************/ """); - sb.AppendLine(); sb.AppendLine("using System;"); sb.AppendLine("using System.Collections.Generic;"); - sb.AppendLine("using Aaru.CommonTypes.Interfaces;"); sb.AppendLine("using Microsoft.Extensions.DependencyInjection;"); + sb.AppendLine("using Aaru.CommonTypes.Interfaces;"); sb.AppendLine(); - sb.AppendLine($"namespace {@namespace};"); + sb.AppendLine($"namespace {registerClass.Namespace};"); sb.AppendLine(); - sb.AppendLine($"public sealed partial class {className} : IPluginRegister"); + sb.AppendLine($"public sealed partial class {registerClass.ClassName} : IPluginRegister"); sb.AppendLine("{"); - if(archives?.Count > 0) + foreach(KeyValuePair kvp in PluginInterfaces) { - sb.AppendLine(" public void RegisterArchivePlugins(IServiceCollection services)"); + string? interfaceName = kvp.Key; + string? methodName = kvp.Value; + + var plugins = pluginInfos.Where(p => p.Interfaces.Contains(interfaceName)) + .Select(p => p.ClassName) + .Distinct() + .ToList(); + + sb.AppendLine($" public void {methodName}(IServiceCollection services)"); sb.AppendLine(" {"); - foreach(string plugin in archives.Distinct()) - sb.AppendLine($" services.AddTransient();"); + foreach(string? plugin in plugins) + sb.AppendLine($" services.AddTransient<{interfaceName}, {plugin}>();"); sb.AppendLine(" }"); } - else - sb.AppendLine(" public void RegisterArchivePlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(checksums?.Count > 0) - { - sb.AppendLine(" public void RegisterChecksumPlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in checksums.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterChecksumPlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(fileSystems?.Count > 0) - { - sb.AppendLine(" public void RegisterFilesystemPlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in fileSystems.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterFilesystemPlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(filters?.Count > 0) - { - sb.AppendLine(" public void RegisterFilterPlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in filters.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterFilterPlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(floppyImagePlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterFloppyImagePlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in floppyImagePlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterFloppyImagePlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(mediaImagePlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterMediaImagePlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in mediaImagePlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterMediaImagePlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(partitionPlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterPartitionPlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in partitionPlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterPartitionPlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(readOnlyFileSystems?.Count > 0) - { - sb.AppendLine(" public void RegisterReadOnlyFilesystemPlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in readOnlyFileSystems.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterReadOnlyFilesystemPlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(writableFloppyImagePlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterWritableFloppyImagePlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in writableFloppyImagePlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterWritableFloppyImagePlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(writableImagePlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterWritableImagePlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in writableImagePlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterWritableImagePlugins(IServiceCollection services) {}"); - - sb.AppendLine(); - - if(byteAddressableImagePlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterByteAddressablePlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in byteAddressableImagePlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterByteAddressablePlugins(IServiceCollection services) {}"); - - if(fluxImagePlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterFluxImagePlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in fluxImagePlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterFluxImagePlugins(IServiceCollection services) {}"); - - if(writableFluxImagePlugins?.Count > 0) - { - sb.AppendLine(" public void RegisterWritableFluxImagePlugins(IServiceCollection services)"); - sb.AppendLine(" {"); - - foreach(string plugin in writableFluxImagePlugins.Distinct()) - sb.AppendLine($" services.AddTransient();"); - - sb.AppendLine(" }"); - } - else - sb.AppendLine(" public void RegisterWritableFluxImagePlugins(IServiceCollection services) {}"); sb.AppendLine("}"); - context.AddSource("Register.g.cs", sb.ToString()); + context.AddSource("Register.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); } -#endregion +#region Nested type: PluginInfo -#region Nested type: PluginFinder - - sealed class PluginFinder : ISyntaxReceiver + private sealed class PluginInfo { - public List Archives { get; } = []; - public List Checksums { get; } = []; - public List FileSystems { get; } = []; - public List Filters { get; } = []; - public List FloppyImagePlugins { get; } = []; - public List MediaImagePlugins { get; } = []; - public List PartitionPlugins { get; } = []; - public List ReadOnlyFileSystems { get; } = []; - public List WritableFloppyImagePlugins { get; } = []; - public List WritableImagePlugins { get; } = []; - public List ByteAddressableImagePlugins { get; } = []; - public List FluxImagePlugins { get; } = []; - public List WritableFluxImagePlugins { get; } = []; - public ClassDeclarationSyntax Register { get; private set; } - -#region ISyntaxReceiver Members - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if(syntaxNode is not ClassDeclarationSyntax plugin) return; - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IPluginRegister") == - true) - Register = plugin; - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IArchive") == - true) - if(!Archives.Contains(plugin.Identifier.Text)) - Archives.Add(plugin.Identifier.Text); - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IChecksum") == - true) - if(!Checksums.Contains(plugin.Identifier.Text)) - Checksums.Add(plugin.Identifier.Text); - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IFilesystem") == - true) - if(!FileSystems.Contains(plugin.Identifier.Text)) - FileSystems.Add(plugin.Identifier.Text); - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IFilter") == - true) - if(!Filters.Contains(plugin.Identifier.Text)) - Filters.Add(plugin.Identifier.Text); - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IFloppyImage") == - true) - if(!FloppyImagePlugins.Contains(plugin.Identifier.Text)) - FloppyImagePlugins.Add(plugin.Identifier.Text); - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IFluxImage") == - true) - if(!FluxImagePlugins.Contains(plugin.Identifier.Text)) - FluxImagePlugins.Add(plugin.Identifier.Text); - - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText is "IMediaImage" - or "IOpticalMediaImage" - or "IFloppyImage" - or "ITapeImage" - or "IFluxImage") == - true) - if(!MediaImagePlugins.Contains(plugin.Identifier.Text)) - MediaImagePlugins.Add(plugin.Identifier.Text); - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IPartition") == - true) - if(!PartitionPlugins.Contains(plugin.Identifier.Text)) - PartitionPlugins.Add(plugin.Identifier.Text); - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IReadOnlyFilesystem") == - true) - { - if(!ReadOnlyFileSystems.Contains(plugin.Identifier.Text)) - ReadOnlyFileSystems.Add(plugin.Identifier.Text); - } - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IWritableFloppyImage") == - true) - { - if(!WritableFloppyImagePlugins.Contains(plugin.Identifier.Text)) - WritableFloppyImagePlugins.Add(plugin.Identifier.Text); - } - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IWritableFluxImage") == - true) - { - if(!WritableFluxImagePlugins.Contains(plugin.Identifier.Text)) - WritableFluxImagePlugins.Add(plugin.Identifier.Text); - } - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText is "IWritableImage" - or "IWritableOpticalImage" - or "IWritableTapeImage" - or "IByteAddressableImage" - or "IWritableFluxImage") == - true) - { - if(!WritableImagePlugins.Contains(plugin.Identifier.Text)) - WritableImagePlugins.Add(plugin.Identifier.Text); - } - - if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier - .ValueText == - "IByteAddressableImage") == - true) - { - if(!ByteAddressableImagePlugins.Contains(plugin.Identifier.Text)) - ByteAddressableImagePlugins.Add(plugin.Identifier.Text); - } - - MediaImagePlugins.AddRange(WritableImagePlugins.Where(t => !ByteAddressableImagePlugins.Contains(t))); - FileSystems.AddRange(ReadOnlyFileSystems); - } - -#endregion + public string? Namespace { get; set; } + public string ClassName { get; set; } = ""; + public bool IsRegister { get; set; } + public List Interfaces { get; } = []; } #endregion