< 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
95%
Covered lines: 157
Uncovered lines: 7
Coverable lines: 164
Total lines: 295
Line coverage: 95.7%
Branch coverage
78%
Covered branches: 52
Total branches: 66
Branch coverage: 78.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GenerateServiceCatalogSource(...)100%11100%
GenerateServicesProperty(...)85%202097.5%
GenerateDecoratorsProperty(...)75%44100%
GenerateHostedServicesProperty(...)75%1212100%
GenerateInterceptedServicesProperty(...)62.5%8890.9%
GenerateOptionsProperty(...)91.66%1212100%
GeneratePluginsProperty(...)100%44100%
GetRelativeSourcePath(...)50%17633.33%

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    {
 55921        var builder = new StringBuilder();
 55922        var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName);
 55923        var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
 24
 55925        builder.AppendLine("// <auto-generated/>");
 55926        builder.AppendLine("// Needlr Service Catalog");
 55927        builder.AppendLine($"// Generated: {timestamp} UTC");
 55928        builder.AppendLine();
 55929        builder.AppendLine("#nullable enable");
 55930        builder.AppendLine();
 55931        builder.AppendLine($"namespace {safeAssemblyName}.Generated;");
 55932        builder.AppendLine();
 33
 55934        breadcrumbs.WriteInlineComment(builder, "", $"ServiceCatalog: {discoveryResult.InjectableTypes.Count} services, 
 35
 55936        builder.AppendLine("/// <summary>");
 55937        builder.AppendLine("/// Compile-time service catalog containing all discovered registrations.");
 55938        builder.AppendLine("/// </summary>");
 55939        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 55940        builder.AppendLine($"public sealed class ServiceCatalog : global::NexusLabs.Needlr.Catalog.IServiceCatalog");
 55941        builder.AppendLine("{");
 55942        builder.AppendLine($"    /// <inheritdoc />");
 55943        builder.AppendLine($"    public string AssemblyName => \"{GeneratorHelpers.EscapeStringLiteral(assemblyName)}\";
 55944        builder.AppendLine();
 55945        builder.AppendLine($"    /// <inheritdoc />");
 55946        builder.AppendLine($"    public string GeneratedAt => \"{timestamp}\";");
 55947        builder.AppendLine();
 48
 49        // Services
 55950        GenerateServicesProperty(builder, discoveryResult.InjectableTypes, projectDirectory);
 51
 52        // Decorators
 55953        GenerateDecoratorsProperty(builder, discoveryResult.Decorators, projectDirectory);
 54
 55        // Hosted Services
 55956        GenerateHostedServicesProperty(builder, discoveryResult.HostedServices, projectDirectory);
 57
 58        // Intercepted Services
 55959        GenerateInterceptedServicesProperty(builder, discoveryResult.InterceptedServices, projectDirectory);
 60
 61        // Options
 55962        GenerateOptionsProperty(builder, discoveryResult.Options, projectDirectory);
 63
 64        // Plugins
 55965        GeneratePluginsProperty(builder, discoveryResult.PluginTypes, projectDirectory);
 66
 55967        builder.AppendLine("}");
 68
 55969        return builder.ToString();
 70    }
 71
 72    private static void GenerateServicesProperty(
 73        StringBuilder builder,
 74        IReadOnlyList<DiscoveredType> types,
 75        string? projectDirectory)
 76    {
 55977        builder.AppendLine($"    /// <inheritdoc />");
 55978        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 55979        builder.AppendLine("    {");
 80
 50725481        foreach (var type in types)
 82        {
 25306883            var shortName = GeneratorHelpers.GetShortTypeName(type.TypeName);
 25306884            var lifetimeStr = type.Lifetime switch
 25306885            {
 25303986                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 2487                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 588                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 089                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 25306890            };
 25306891            var sourceFilePath = GetRelativeSourcePath(type.SourceFilePath, projectDirectory);
 25306892            var sourceFilePathLiteral = sourceFilePath != null
 25306893                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 25306894                : "null";
 95
 96            // Build interfaces array (for backwards compat)
 25346397            var interfacesArray = $"new string[] {{ {string.Join(", ", type.InterfaceNames.Select(i => $"\"{GeneratorHel
 98
 99            // Build interface entries array with locations
 253068100            var interfaceEntriesBuilder = new StringBuilder();
 253068101            interfaceEntriesBuilder.Append("new global::NexusLabs.Needlr.Catalog.InterfaceEntry[] { ");
 506926102            foreach (var ifaceInfo in type.InterfaceInfos)
 103            {
 395104                var ifaceFilePath = GetRelativeSourcePath(ifaceInfo.SourceFilePath, projectDirectory);
 395105                var ifaceFilePathLiteral = ifaceFilePath != null
 395106                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(ifaceFilePath)}\""
 395107                    : "null";
 395108                interfaceEntriesBuilder.Append($"new global::NexusLabs.Needlr.Catalog.InterfaceEntry(\"{GeneratorHelpers
 109            }
 253068110            interfaceEntriesBuilder.Append('}');
 111
 112            // Build constructor parameters array
 253068113            var constructorParamsBuilder = new StringBuilder();
 253068114            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 713668115            foreach (var param in type.ConstructorParameters)
 116            {
 103766117                var serviceKeyLiteral = param.ServiceKey != null
 103766118                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 103766119                    : "null";
 103766120                var paramName = param.ParameterName ?? "unknown";
 103766121                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 122            }
 253068123            constructorParamsBuilder.Append('}');
 124
 125            // Build service keys array
 253073126            var serviceKeysArray = $"new string[] {{ {string.Join(", ", type.ServiceKeys.Select(k => $"\"{GeneratorHelpe
 127
 253068128            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.ServiceCatalogEntry(\"{GeneratorHelpers.Es
 129        }
 130
 559131        builder.AppendLine("    };");
 559132        builder.AppendLine();
 559133    }
 134
 135    private static void GenerateDecoratorsProperty(
 136        StringBuilder builder,
 137        IReadOnlyList<DiscoveredDecorator> decorators,
 138        string? projectDirectory)
 139    {
 559140        builder.AppendLine($"    /// <inheritdoc />");
 559141        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 559142        builder.AppendLine("    {");
 143
 1170144        foreach (var decorator in decorators)
 145        {
 26146            var shortName = GeneratorHelpers.GetShortTypeName(decorator.DecoratorTypeName);
 26147            var sourceFilePath = GetRelativeSourcePath(decorator.SourceFilePath, projectDirectory);
 26148            var sourceFilePathLiteral = sourceFilePath != null
 26149                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 26150                : "null";
 151
 26152            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.DecoratorCatalogEntry(\"{GeneratorHelpers.
 153        }
 154
 559155        builder.AppendLine("    };");
 559156        builder.AppendLine();
 559157    }
 158
 159    private static void GenerateHostedServicesProperty(
 160        StringBuilder builder,
 161        IReadOnlyList<DiscoveredHostedService> hostedServices,
 162        string? projectDirectory)
 163    {
 559164        builder.AppendLine($"    /// <inheritdoc />");
 559165        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 559166        builder.AppendLine("    {");
 167
 1134168        foreach (var hosted in hostedServices)
 169        {
 8170            var shortName = GeneratorHelpers.GetShortTypeName(hosted.TypeName);
 8171            var sourceFilePath = GetRelativeSourcePath(hosted.SourceFilePath, projectDirectory);
 8172            var sourceFilePathLiteral = sourceFilePath != null
 8173                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 8174                : "null";
 175
 176            // Build constructor parameters array
 8177            var constructorParamsBuilder = new StringBuilder();
 8178            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 20179            foreach (var param in hosted.ConstructorParameters)
 180            {
 2181                var serviceKeyLiteral = param.ServiceKey != null
 2182                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 2183                    : "null";
 2184                var paramName = param.ParameterName ?? "unknown";
 2185                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 186            }
 8187            constructorParamsBuilder.Append('}');
 188
 8189            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.HostedServiceCatalogEntry(\"{GeneratorHelp
 190        }
 191
 559192        builder.AppendLine("    };");
 559193        builder.AppendLine();
 559194    }
 195
 196    private static void GenerateInterceptedServicesProperty(
 197        StringBuilder builder,
 198        IReadOnlyList<DiscoveredInterceptedService> interceptedServices,
 199        string? projectDirectory)
 200    {
 559201        builder.AppendLine($"    /// <inheritdoc />");
 559202        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 559203        builder.AppendLine("    {");
 204
 1146205        foreach (var intercepted in interceptedServices)
 206        {
 14207            var shortName = GeneratorHelpers.GetShortTypeName(intercepted.TypeName);
 14208            var lifetimeStr = intercepted.Lifetime switch
 14209            {
 3210                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 11211                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 0212                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 0213                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 14214            };
 14215            var sourceFilePath = GetRelativeSourcePath(intercepted.SourceFilePath, projectDirectory);
 14216            var sourceFilePathLiteral = sourceFilePath != null
 14217                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 14218                : "null";
 219
 28220            var interfacesArray = $"new string[] {{ {string.Join(", ", intercepted.InterfaceNames.Select(i => $"\"{Gener
 31221            var interceptorsArray = $"new string[] {{ {string.Join(", ", intercepted.AllInterceptorTypeNames.Select(i =>
 222
 14223            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.InterceptedServiceCatalogEntry(\"{Generato
 224        }
 225
 559226        builder.AppendLine("    };");
 559227        builder.AppendLine();
 559228    }
 229
 230    private static void GenerateOptionsProperty(
 231        StringBuilder builder,
 232        IReadOnlyList<DiscoveredOptions> options,
 233        string? projectDirectory)
 234    {
 559235        builder.AppendLine($"    /// <inheritdoc />");
 559236        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 559237        builder.AppendLine("    {");
 238
 1466239        foreach (var opt in options)
 240        {
 174241            var shortName = GeneratorHelpers.GetShortTypeName(opt.TypeName);
 174242            var sourceFilePath = GetRelativeSourcePath(opt.SourceFilePath, projectDirectory);
 174243            var sourceFilePathLiteral = sourceFilePath != null
 174244                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 174245                : "null";
 174246            var nameLiteral = opt.Name != null
 174247                ? $"\"{GeneratorHelpers.EscapeStringLiteral(opt.Name)}\""
 174248                : "null";
 249
 174250            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.OptionsCatalogEntry(\"{GeneratorHelpers.Es
 251        }
 252
 559253        builder.AppendLine("    };");
 559254        builder.AppendLine();
 559255    }
 256
 257    private static void GeneratePluginsProperty(
 258        StringBuilder builder,
 259        IReadOnlyList<DiscoveredPlugin> plugins,
 260        string? projectDirectory)
 261    {
 559262        builder.AppendLine($"    /// <inheritdoc />");
 559263        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 559264        builder.AppendLine("    {");
 265
 4406266        foreach (var plugin in plugins)
 267        {
 1644268            var shortName = GeneratorHelpers.GetShortTypeName(plugin.TypeName);
 1644269            var sourceFilePath = GetRelativeSourcePath(plugin.SourceFilePath, projectDirectory);
 1644270            var sourceFilePathLiteral = sourceFilePath != null
 1644271                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 1644272                : "null";
 273
 3300274            var interfacesArray = $"new string[] {{ {string.Join(", ", plugin.InterfaceNames.Select(i => $"\"{GeneratorH
 275
 1644276            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.PluginCatalogEntry(\"{GeneratorHelpers.Esc
 277        }
 278
 559279        builder.AppendLine("    };");
 559280    }
 281
 282    private static string? GetRelativeSourcePath(string? fullPath, string? projectDirectory)
 283    {
 255329284        if (fullPath == null || projectDirectory == null)
 255329285            return fullPath;
 286
 0287        if (fullPath.StartsWith(projectDirectory, StringComparison.OrdinalIgnoreCase))
 288        {
 0289            var relative = fullPath.Substring(projectDirectory.Length);
 0290            return relative.TrimStart('/', '\\');
 291        }
 292
 0293        return fullPath;
 294    }
 295}