< 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: 155
Uncovered lines: 3
Coverable lines: 158
Total lines: 290
Line coverage: 98.1%
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        GeneratedRegistrationPlan registrationPlan,
 17        string? projectDirectory,
 18        BreadcrumbWriter breadcrumbs)
 19    {
 59020        var builder = new StringBuilder();
 59021        var discoveryResult = registrationPlan.DiscoveryResult;
 59022        var assemblyName = registrationPlan.AssemblyName;
 59023        var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName);
 24
 59025        builder.AppendLine("// <auto-generated/>");
 59026        builder.AppendLine("// Needlr Service Catalog");
 59027        builder.AppendLine();
 59028        builder.AppendLine("#nullable enable");
 59029        builder.AppendLine();
 59030        builder.AppendLine($"namespace {safeAssemblyName}.Generated;");
 59031        builder.AppendLine();
 32
 59033        breadcrumbs.WriteInlineComment(builder, "", $"ServiceCatalog: {GeneratorHelpers.Literal(discoveryResult.Injectab
 34
 59035        builder.AppendLine("/// <summary>");
 59036        builder.AppendLine("/// Compile-time service catalog containing all discovered registrations.");
 59037        builder.AppendLine("/// </summary>");
 59038        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 59039        builder.AppendLine($"public sealed class ServiceCatalog : global::NexusLabs.Needlr.Catalog.IServiceCatalog");
 59040        builder.AppendLine("{");
 59041        builder.AppendLine($"    /// <inheritdoc />");
 59042        builder.AppendLine($"    public string AssemblyName => \"{GeneratorHelpers.EscapeStringLiteral(assemblyName)}\";
 59043        builder.AppendLine();
 44
 45        // Services
 59046        GenerateServicesProperty(builder, discoveryResult.InjectableTypes, projectDirectory);
 47
 48        // Decorators
 59049        GenerateDecoratorsProperty(builder, discoveryResult.Decorators, projectDirectory);
 50
 51        // Hosted Services
 59052        GenerateHostedServicesProperty(builder, discoveryResult.HostedServices, projectDirectory);
 53
 54        // Intercepted Services
 59055        GenerateInterceptedServicesProperty(builder, discoveryResult.InterceptedServices, projectDirectory);
 56
 57        // Options
 59058        GenerateOptionsProperty(builder, discoveryResult.Options, projectDirectory);
 59
 60        // Plugins
 59061        GeneratePluginsProperty(builder, discoveryResult.PluginTypes, projectDirectory);
 62
 59063        builder.AppendLine("}");
 64
 59065        return builder.ToString();
 66    }
 67
 68    private static void GenerateServicesProperty(
 69        StringBuilder builder,
 70        IReadOnlyList<DiscoveredType> types,
 71        string? projectDirectory)
 72    {
 59073        builder.AppendLine($"    /// <inheritdoc />");
 59074        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 59075        builder.AppendLine("    {");
 76
 53279477        foreach (var type in types)
 78        {
 26580779            var shortName = GeneratorHelpers.GetShortTypeName(type.TypeName);
 26580780            var lifetimeStr = type.Lifetime switch
 26580781            {
 26577682                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 2583                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 684                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 085                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 26580786            };
 26580787            var sourceFilePath = GetRelativeSourcePath(type.SourceFilePath, projectDirectory);
 26580788            var sourceFilePathLiteral = sourceFilePath != null
 26580789                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 26580790                : "null";
 91
 92            // Build interfaces array (for backwards compat)
 26622893            var interfacesArray = $"new string[] {{ {string.Join(", ", type.InterfaceNames.Select(i => $"\"{GeneratorHel
 94
 95            // Build interface entries array with locations
 26580796            var interfaceEntriesBuilder = new StringBuilder();
 26580797            interfaceEntriesBuilder.Append("new global::NexusLabs.Needlr.Catalog.InterfaceEntry[] { ");
 53245698            foreach (var ifaceInfo in type.InterfaceInfos)
 99            {
 421100                var ifaceFilePath = GetRelativeSourcePath(ifaceInfo.SourceFilePath, projectDirectory);
 421101                var ifaceFilePathLiteral = ifaceFilePath != null
 421102                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(ifaceFilePath)}\""
 421103                    : "null";
 421104                interfaceEntriesBuilder.Append($"new global::NexusLabs.Needlr.Catalog.InterfaceEntry(\"{GeneratorHelpers
 105            }
 265807106            interfaceEntriesBuilder.Append('}');
 107
 108            // Build constructor parameters array
 265807109            var constructorParamsBuilder = new StringBuilder();
 265807110            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 749606111            foreach (var param in type.ConstructorParameters)
 112            {
 108996113                var serviceKeyLiteral = param.ServiceKey != null
 108996114                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 108996115                    : "null";
 108996116                var paramName = param.ParameterName ?? "unknown";
 108996117                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 118            }
 265807119            constructorParamsBuilder.Append('}');
 120
 121            // Build service keys array
 265813122            var serviceKeysArray = $"new string[] {{ {string.Join(", ", type.ServiceKeys.Select(k => $"\"{GeneratorHelpe
 123
 265807124            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.ServiceCatalogEntry(\"{GeneratorHelpers.Es
 125        }
 126
 590127        builder.AppendLine("    };");
 590128        builder.AppendLine();
 590129    }
 130
 131    private static void GenerateDecoratorsProperty(
 132        StringBuilder builder,
 133        IReadOnlyList<DiscoveredDecorator> decorators,
 134        string? projectDirectory)
 135    {
 590136        builder.AppendLine($"    /// <inheritdoc />");
 590137        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 590138        builder.AppendLine("    {");
 139
 1274140        foreach (var decorator in decorators)
 141        {
 47142            var shortName = GeneratorHelpers.GetShortTypeName(decorator.DecoratorTypeName);
 47143            var sourceFilePath = GetRelativeSourcePath(decorator.SourceFilePath, projectDirectory);
 47144            var sourceFilePathLiteral = sourceFilePath != null
 47145                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 47146                : "null";
 147
 47148            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.DecoratorCatalogEntry(\"{GeneratorHelpers.
 149        }
 150
 590151        builder.AppendLine("    };");
 590152        builder.AppendLine();
 590153    }
 154
 155    private static void GenerateHostedServicesProperty(
 156        StringBuilder builder,
 157        IReadOnlyList<DiscoveredHostedService> hostedServices,
 158        string? projectDirectory)
 159    {
 590160        builder.AppendLine($"    /// <inheritdoc />");
 590161        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 590162        builder.AppendLine("    {");
 163
 1198164        foreach (var hosted in hostedServices)
 165        {
 9166            var shortName = GeneratorHelpers.GetShortTypeName(hosted.TypeName);
 9167            var sourceFilePath = GetRelativeSourcePath(hosted.SourceFilePath, projectDirectory);
 9168            var sourceFilePathLiteral = sourceFilePath != null
 9169                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 9170                : "null";
 171
 172            // Build constructor parameters array
 9173            var constructorParamsBuilder = new StringBuilder();
 9174            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 22175            foreach (var param in hosted.ConstructorParameters)
 176            {
 2177                var serviceKeyLiteral = param.ServiceKey != null
 2178                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 2179                    : "null";
 2180                var paramName = param.ParameterName ?? "unknown";
 2181                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 182            }
 9183            constructorParamsBuilder.Append('}');
 184
 9185            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.HostedServiceCatalogEntry(\"{GeneratorHelp
 186        }
 187
 590188        builder.AppendLine("    };");
 590189        builder.AppendLine();
 590190    }
 191
 192    private static void GenerateInterceptedServicesProperty(
 193        StringBuilder builder,
 194        IReadOnlyList<DiscoveredInterceptedService> interceptedServices,
 195        string? projectDirectory)
 196    {
 590197        builder.AppendLine($"    /// <inheritdoc />");
 590198        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 590199        builder.AppendLine("    {");
 200
 1210201        foreach (var intercepted in interceptedServices)
 202        {
 15203            var shortName = GeneratorHelpers.GetShortTypeName(intercepted.TypeName);
 15204            var lifetimeStr = intercepted.Lifetime switch
 15205            {
 4206                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 11207                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 0208                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 0209                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 15210            };
 15211            var sourceFilePath = GetRelativeSourcePath(intercepted.SourceFilePath, projectDirectory);
 15212            var sourceFilePathLiteral = sourceFilePath != null
 15213                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 15214                : "null";
 215
 30216            var interfacesArray = $"new string[] {{ {string.Join(", ", intercepted.InterfaceNames.Select(i => $"\"{Gener
 33217            var interceptorsArray = $"new string[] {{ {string.Join(", ", intercepted.AllInterceptorTypeNames.Select(i =>
 218
 15219            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.InterceptedServiceCatalogEntry(\"{Generato
 220        }
 221
 590222        builder.AppendLine("    };");
 590223        builder.AppendLine();
 590224    }
 225
 226    private static void GenerateOptionsProperty(
 227        StringBuilder builder,
 228        IReadOnlyList<DiscoveredOptions> options,
 229        string? projectDirectory)
 230    {
 590231        builder.AppendLine($"    /// <inheritdoc />");
 590232        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 590233        builder.AppendLine("    {");
 234
 1532235        foreach (var opt in options)
 236        {
 176237            var shortName = GeneratorHelpers.GetShortTypeName(opt.TypeName);
 176238            var sourceFilePath = GetRelativeSourcePath(opt.SourceFilePath, projectDirectory);
 176239            var sourceFilePathLiteral = sourceFilePath != null
 176240                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 176241                : "null";
 176242            var nameLiteral = opt.Name != null
 176243                ? $"\"{GeneratorHelpers.EscapeStringLiteral(opt.Name)}\""
 176244                : "null";
 245
 176246            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.OptionsCatalogEntry(\"{GeneratorHelpers.Es
 247        }
 248
 590249        builder.AppendLine("    };");
 590250        builder.AppendLine();
 590251    }
 252
 253    private static void GeneratePluginsProperty(
 254        StringBuilder builder,
 255        IReadOnlyList<DiscoveredPlugin> plugins,
 256        string? projectDirectory)
 257    {
 590258        builder.AppendLine($"    /// <inheritdoc />");
 590259        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 590260        builder.AppendLine("    {");
 261
 4650262        foreach (var plugin in plugins)
 263        {
 1735264            var shortName = GeneratorHelpers.GetShortTypeName(plugin.TypeName);
 1735265            var sourceFilePath = GetRelativeSourcePath(plugin.SourceFilePath, projectDirectory);
 1735266            var sourceFilePathLiteral = sourceFilePath != null
 1735267                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 1735268                : "null";
 269
 3484270            var interfacesArray = $"new string[] {{ {string.Join(", ", plugin.InterfaceNames.Select(i => $"\"{GeneratorH
 271
 1735272            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.PluginCatalogEntry(\"{GeneratorHelpers.Esc
 273        }
 274
 590275        builder.AppendLine("    };");
 590276    }
 277
 278    private static string? GetRelativeSourcePath(string? fullPath, string? projectDirectory)
 279    {
 268210280        if (string.IsNullOrEmpty(fullPath))
 281        {
 268198282            return null;
 283        }
 284
 285        // Delegates to the shared helper so the catalog cannot drift from every other
 286        // emitter. The shared helper normalizes separators to '/' and falls back to the
 287        // bare file name rather than leaking an absolute build-machine path.
 12288        return BreadcrumbWriter.GetRelativeSourcePath(fullPath, projectDirectory);
 289    }
 290}