< Summary

Information
Class: NexusLabs.Needlr.Generators.CodeGen.ServiceCatalogCodeGenerator
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/CodeGen/ServiceCatalogCodeGenerator.cs
Line coverage
98%
Covered lines: 153
Uncovered lines: 3
Coverable lines: 156
Total lines: 289
Line coverage: 98%
Branch coverage
82%
Covered branches: 51
Total branches: 62
Branch coverage: 82.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/CodeGen/ServiceCatalogCodeGenerator.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Text;
 5
 6using NexusLabs.Needlr.Generators.Models;
 7
 8namespace NexusLabs.Needlr.Generators.CodeGen;
 9
 10/// <summary>
 11/// Generates the ServiceCatalog implementation from DiscoveryResult.
 12/// </summary>
 13internal static class ServiceCatalogCodeGenerator
 14{
 15    internal static string GenerateServiceCatalogSource(
 16        DiscoveryResult discoveryResult,
 17        string assemblyName,
 18        string? projectDirectory,
 19        BreadcrumbWriter breadcrumbs)
 20    {
 58021        var builder = new StringBuilder();
 58022        var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName);
 23
 58024        builder.AppendLine("// <auto-generated/>");
 58025        builder.AppendLine("// Needlr Service Catalog");
 58026        builder.AppendLine();
 58027        builder.AppendLine("#nullable enable");
 58028        builder.AppendLine();
 58029        builder.AppendLine($"namespace {safeAssemblyName}.Generated;");
 58030        builder.AppendLine();
 31
 58032        breadcrumbs.WriteInlineComment(builder, "", $"ServiceCatalog: {GeneratorHelpers.Literal(discoveryResult.Injectab
 33
 58034        builder.AppendLine("/// <summary>");
 58035        builder.AppendLine("/// Compile-time service catalog containing all discovered registrations.");
 58036        builder.AppendLine("/// </summary>");
 58037        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 58038        builder.AppendLine($"public sealed class ServiceCatalog : global::NexusLabs.Needlr.Catalog.IServiceCatalog");
 58039        builder.AppendLine("{");
 58040        builder.AppendLine($"    /// <inheritdoc />");
 58041        builder.AppendLine($"    public string AssemblyName => \"{GeneratorHelpers.EscapeStringLiteral(assemblyName)}\";
 58042        builder.AppendLine();
 43
 44        // Services
 58045        GenerateServicesProperty(builder, discoveryResult.InjectableTypes, projectDirectory);
 46
 47        // Decorators
 58048        GenerateDecoratorsProperty(builder, discoveryResult.Decorators, projectDirectory);
 49
 50        // Hosted Services
 58051        GenerateHostedServicesProperty(builder, discoveryResult.HostedServices, projectDirectory);
 52
 53        // Intercepted Services
 58054        GenerateInterceptedServicesProperty(builder, discoveryResult.InterceptedServices, projectDirectory);
 55
 56        // Options
 58057        GenerateOptionsProperty(builder, discoveryResult.Options, projectDirectory);
 58
 59        // Plugins
 58060        GeneratePluginsProperty(builder, discoveryResult.PluginTypes, projectDirectory);
 61
 58062        builder.AppendLine("}");
 63
 58064        return builder.ToString();
 65    }
 66
 67    private static void GenerateServicesProperty(
 68        StringBuilder builder,
 69        IReadOnlyList<DiscoveredType> types,
 70        string? projectDirectory)
 71    {
 58072        builder.AppendLine($"    /// <inheritdoc />");
 58073        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 58074        builder.AppendLine("    {");
 75
 53274676        foreach (var type in types)
 77        {
 26579378            var shortName = GeneratorHelpers.GetShortTypeName(type.TypeName);
 26579379            var lifetimeStr = type.Lifetime switch
 26579380            {
 26576481                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 2482                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 583                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 084                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 26579385            };
 26579386            var sourceFilePath = GetRelativeSourcePath(type.SourceFilePath, projectDirectory);
 26579387            var sourceFilePathLiteral = sourceFilePath != null
 26579388                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 26579389                : "null";
 90
 91            // Build interfaces array (for backwards compat)
 26620892            var interfacesArray = $"new string[] {{ {string.Join(", ", type.InterfaceNames.Select(i => $"\"{GeneratorHel
 93
 94            // Build interface entries array with locations
 26579395            var interfaceEntriesBuilder = new StringBuilder();
 26579396            interfaceEntriesBuilder.Append("new global::NexusLabs.Needlr.Catalog.InterfaceEntry[] { ");
 53241697            foreach (var ifaceInfo in type.InterfaceInfos)
 98            {
 41599                var ifaceFilePath = GetRelativeSourcePath(ifaceInfo.SourceFilePath, projectDirectory);
 415100                var ifaceFilePathLiteral = ifaceFilePath != null
 415101                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(ifaceFilePath)}\""
 415102                    : "null";
 415103                interfaceEntriesBuilder.Append($"new global::NexusLabs.Needlr.Catalog.InterfaceEntry(\"{GeneratorHelpers
 104            }
 265793105            interfaceEntriesBuilder.Append('}');
 106
 107            // Build constructor parameters array
 265793108            var constructorParamsBuilder = new StringBuilder();
 265793109            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 749574110            foreach (var param in type.ConstructorParameters)
 111            {
 108994112                var serviceKeyLiteral = param.ServiceKey != null
 108994113                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 108994114                    : "null";
 108994115                var paramName = param.ParameterName ?? "unknown";
 108994116                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 117            }
 265793118            constructorParamsBuilder.Append('}');
 119
 120            // Build service keys array
 265798121            var serviceKeysArray = $"new string[] {{ {string.Join(", ", type.ServiceKeys.Select(k => $"\"{GeneratorHelpe
 122
 265793123            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.ServiceCatalogEntry(\"{GeneratorHelpers.Es
 124        }
 125
 580126        builder.AppendLine("    };");
 580127        builder.AppendLine();
 580128    }
 129
 130    private static void GenerateDecoratorsProperty(
 131        StringBuilder builder,
 132        IReadOnlyList<DiscoveredDecorator> decorators,
 133        string? projectDirectory)
 134    {
 580135        builder.AppendLine($"    /// <inheritdoc />");
 580136        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 580137        builder.AppendLine("    {");
 138
 1252139        foreach (var decorator in decorators)
 140        {
 46141            var shortName = GeneratorHelpers.GetShortTypeName(decorator.DecoratorTypeName);
 46142            var sourceFilePath = GetRelativeSourcePath(decorator.SourceFilePath, projectDirectory);
 46143            var sourceFilePathLiteral = sourceFilePath != null
 46144                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 46145                : "null";
 146
 46147            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.DecoratorCatalogEntry(\"{GeneratorHelpers.
 148        }
 149
 580150        builder.AppendLine("    };");
 580151        builder.AppendLine();
 580152    }
 153
 154    private static void GenerateHostedServicesProperty(
 155        StringBuilder builder,
 156        IReadOnlyList<DiscoveredHostedService> hostedServices,
 157        string? projectDirectory)
 158    {
 580159        builder.AppendLine($"    /// <inheritdoc />");
 580160        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 580161        builder.AppendLine("    {");
 162
 1176163        foreach (var hosted in hostedServices)
 164        {
 8165            var shortName = GeneratorHelpers.GetShortTypeName(hosted.TypeName);
 8166            var sourceFilePath = GetRelativeSourcePath(hosted.SourceFilePath, projectDirectory);
 8167            var sourceFilePathLiteral = sourceFilePath != null
 8168                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 8169                : "null";
 170
 171            // Build constructor parameters array
 8172            var constructorParamsBuilder = new StringBuilder();
 8173            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 20174            foreach (var param in hosted.ConstructorParameters)
 175            {
 2176                var serviceKeyLiteral = param.ServiceKey != null
 2177                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 2178                    : "null";
 2179                var paramName = param.ParameterName ?? "unknown";
 2180                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 181            }
 8182            constructorParamsBuilder.Append('}');
 183
 8184            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.HostedServiceCatalogEntry(\"{GeneratorHelp
 185        }
 186
 580187        builder.AppendLine("    };");
 580188        builder.AppendLine();
 580189    }
 190
 191    private static void GenerateInterceptedServicesProperty(
 192        StringBuilder builder,
 193        IReadOnlyList<DiscoveredInterceptedService> interceptedServices,
 194        string? projectDirectory)
 195    {
 580196        builder.AppendLine($"    /// <inheritdoc />");
 580197        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 580198        builder.AppendLine("    {");
 199
 1188200        foreach (var intercepted in interceptedServices)
 201        {
 14202            var shortName = GeneratorHelpers.GetShortTypeName(intercepted.TypeName);
 14203            var lifetimeStr = intercepted.Lifetime switch
 14204            {
 3205                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 11206                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 0207                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 0208                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 14209            };
 14210            var sourceFilePath = GetRelativeSourcePath(intercepted.SourceFilePath, projectDirectory);
 14211            var sourceFilePathLiteral = sourceFilePath != null
 14212                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 14213                : "null";
 214
 28215            var interfacesArray = $"new string[] {{ {string.Join(", ", intercepted.InterfaceNames.Select(i => $"\"{Gener
 31216            var interceptorsArray = $"new string[] {{ {string.Join(", ", intercepted.AllInterceptorTypeNames.Select(i =>
 217
 14218            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.InterceptedServiceCatalogEntry(\"{Generato
 219        }
 220
 580221        builder.AppendLine("    };");
 580222        builder.AppendLine();
 580223    }
 224
 225    private static void GenerateOptionsProperty(
 226        StringBuilder builder,
 227        IReadOnlyList<DiscoveredOptions> options,
 228        string? projectDirectory)
 229    {
 580230        builder.AppendLine($"    /// <inheritdoc />");
 580231        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 580232        builder.AppendLine("    {");
 233
 1508234        foreach (var opt in options)
 235        {
 174236            var shortName = GeneratorHelpers.GetShortTypeName(opt.TypeName);
 174237            var sourceFilePath = GetRelativeSourcePath(opt.SourceFilePath, projectDirectory);
 174238            var sourceFilePathLiteral = sourceFilePath != null
 174239                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 174240                : "null";
 174241            var nameLiteral = opt.Name != null
 174242                ? $"\"{GeneratorHelpers.EscapeStringLiteral(opt.Name)}\""
 174243                : "null";
 244
 174245            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.OptionsCatalogEntry(\"{GeneratorHelpers.Es
 246        }
 247
 580248        builder.AppendLine("    };");
 580249        builder.AppendLine();
 580250    }
 251
 252    private static void GeneratePluginsProperty(
 253        StringBuilder builder,
 254        IReadOnlyList<DiscoveredPlugin> plugins,
 255        string? projectDirectory)
 256    {
 580257        builder.AppendLine($"    /// <inheritdoc />");
 580258        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 580259        builder.AppendLine("    {");
 260
 4614261        foreach (var plugin in plugins)
 262        {
 1727263            var shortName = GeneratorHelpers.GetShortTypeName(plugin.TypeName);
 1727264            var sourceFilePath = GetRelativeSourcePath(plugin.SourceFilePath, projectDirectory);
 1727265            var sourceFilePathLiteral = sourceFilePath != null
 1727266                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 1727267                : "null";
 268
 3466269            var interfacesArray = $"new string[] {{ {string.Join(", ", plugin.InterfaceNames.Select(i => $"\"{GeneratorH
 270
 1727271            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.PluginCatalogEntry(\"{GeneratorHelpers.Esc
 272        }
 273
 580274        builder.AppendLine("    };");
 580275    }
 276
 277    private static string? GetRelativeSourcePath(string? fullPath, string? projectDirectory)
 278    {
 268177279        if (string.IsNullOrEmpty(fullPath))
 280        {
 268165281            return null;
 282        }
 283
 284        // Delegates to the shared helper so the catalog cannot drift from every other
 285        // emitter. The shared helper normalizes separators to '/' and falls back to the
 286        // bare file name rather than leaking an absolute build-machine path.
 12287        return BreadcrumbWriter.GetRelativeSourcePath(fullPath, projectDirectory);
 288    }
 289}