< Summary

Information
Class: NexusLabs.Needlr.Generators.Models.DiscoveredOptions
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/Models/DiscoveryModels.cs
Line coverage
96%
Covered lines: 31
Uncovered lines: 1
Coverable lines: 32
Total lines: 735
Line coverage: 96.8%
Branch coverage
62%
Covered branches: 5
Total branches: 8
Branch coverage: 62.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/Models/DiscoveryModels.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Linq;
 3using Microsoft.CodeAnalysis;
 4
 5namespace NexusLabs.Needlr.Generators.Models;
 6
 7/// <summary>
 8/// Information about a discovered injectable type.
 9/// </summary>
 10internal readonly struct DiscoveredType
 11{
 12    public DiscoveredType(string typeName, string[] interfaceNames, string assemblyName, GeneratorLifetime lifetime, Typ
 13    {
 14        TypeName = typeName;
 15        InterfaceNames = interfaceNames;
 16        AssemblyName = assemblyName;
 17        Lifetime = lifetime;
 18        ConstructorParameters = constructorParameters;
 19        ServiceKeys = serviceKeys;
 20        SourceFilePath = sourceFilePath;
 21        IsDisposable = isDisposable;
 22    }
 23
 24    public string TypeName { get; }
 25    public string[] InterfaceNames { get; }
 26    public string AssemblyName { get; }
 27    public GeneratorLifetime Lifetime { get; }
 28    public TypeDiscoveryHelper.ConstructorParameterInfo[] ConstructorParameters { get; }
 29    /// <summary>
 30    /// Service keys from [Keyed] attributes on this type.
 31    /// </summary>
 32    public string[] ServiceKeys { get; }
 33    public string? SourceFilePath { get; }
 34    /// <summary>
 35    /// True if this type implements IDisposable or IAsyncDisposable.
 36    /// </summary>
 37    public bool IsDisposable { get; }
 38
 39    /// <summary>
 40    /// Gets the constructor parameter types (for backward compatibility with existing code paths).
 41    /// </summary>
 42    public string[] ConstructorParameterTypes => ConstructorParameters.Select(p => p.TypeName).ToArray();
 43
 44    /// <summary>
 45    /// True if any constructor parameters are keyed services.
 46    /// </summary>
 47    public bool HasKeyedParameters => ConstructorParameters.Any(p => p.IsKeyed);
 48
 49    /// <summary>
 50    /// True if this type has [Keyed] attributes for keyed registration.
 51    /// </summary>
 52    public bool IsKeyed => ServiceKeys.Length > 0;
 53}
 54
 55/// <summary>
 56/// Information about a discovered plugin type (implements INeedlrPlugin interfaces).
 57/// </summary>
 58internal readonly struct DiscoveredPlugin
 59{
 60    public DiscoveredPlugin(string typeName, string[] interfaceNames, string assemblyName, string[] attributeNames, stri
 61    {
 62        TypeName = typeName;
 63        InterfaceNames = interfaceNames;
 64        AssemblyName = assemblyName;
 65        AttributeNames = attributeNames;
 66        SourceFilePath = sourceFilePath;
 67        Order = order;
 68    }
 69
 70    public string TypeName { get; }
 71    public string[] InterfaceNames { get; }
 72    public string AssemblyName { get; }
 73    public string[] AttributeNames { get; }
 74    public string? SourceFilePath { get; }
 75    public int Order { get; }
 76}
 77
 78/// <summary>
 79/// Information about a closed-generic decorator (from [DecoratorFor&lt;T&gt;]).
 80/// </summary>
 81internal readonly struct DiscoveredDecorator
 82{
 83    public DiscoveredDecorator(string decoratorTypeName, string serviceTypeName, int order, string assemblyName, string?
 84    {
 85        DecoratorTypeName = decoratorTypeName;
 86        ServiceTypeName = serviceTypeName;
 87        Order = order;
 88        AssemblyName = assemblyName;
 89        SourceFilePath = sourceFilePath;
 90    }
 91
 92    public string DecoratorTypeName { get; }
 93    public string ServiceTypeName { get; }
 94    public int Order { get; }
 95    public string AssemblyName { get; }
 96    public string? SourceFilePath { get; }
 97}
 98
 99/// <summary>
 100/// Information about a discovered hosted service (BackgroundService or IHostedService implementation).
 101/// </summary>
 102internal readonly struct DiscoveredHostedService
 103{
 104    public DiscoveredHostedService(
 105        string typeName,
 106        string assemblyName,
 107        GeneratorLifetime lifetime,
 108        TypeDiscoveryHelper.ConstructorParameterInfo[] constructorParameters,
 109        string? sourceFilePath = null)
 110    {
 111        TypeName = typeName;
 112        AssemblyName = assemblyName;
 113        Lifetime = lifetime;
 114        ConstructorParameters = constructorParameters;
 115        SourceFilePath = sourceFilePath;
 116    }
 117
 118    public string TypeName { get; }
 119    public string AssemblyName { get; }
 120    public GeneratorLifetime Lifetime { get; }
 121    public TypeDiscoveryHelper.ConstructorParameterInfo[] ConstructorParameters { get; }
 122    public string? SourceFilePath { get; }
 123}
 124
 125/// <summary>
 126/// Information about an open-generic decorator (from [OpenDecoratorFor(typeof(IHandler&lt;&gt;))]).
 127/// </summary>
 128internal readonly struct DiscoveredOpenDecorator
 129{
 130    public DiscoveredOpenDecorator(
 131        INamedTypeSymbol decoratorType,
 132        INamedTypeSymbol openGenericInterface,
 133        int order,
 134        string assemblyName,
 135        string? sourceFilePath = null)
 136    {
 137        DecoratorType = decoratorType;
 138        OpenGenericInterface = openGenericInterface;
 139        Order = order;
 140        AssemblyName = assemblyName;
 141        SourceFilePath = sourceFilePath;
 142    }
 143
 144    public INamedTypeSymbol DecoratorType { get; }
 145    public INamedTypeSymbol OpenGenericInterface { get; }
 146    public int Order { get; }
 147    public string AssemblyName { get; }
 148    public string? SourceFilePath { get; }
 149}
 150
 151/// <summary>
 152/// Information about a type that would be injectable but is inaccessible (internal/private).
 153/// </summary>
 154internal readonly struct InaccessibleType
 155{
 156    public InaccessibleType(string typeName, string assemblyName)
 157    {
 158        TypeName = typeName;
 159        AssemblyName = assemblyName;
 160    }
 161
 162    public string TypeName { get; }
 163    public string AssemblyName { get; }
 164}
 165
 166/// <summary>
 167/// Information about a plugin type from a referenced assembly that's missing [GenerateTypeRegistry].
 168/// </summary>
 169internal readonly struct MissingTypeRegistryPlugin
 170{
 171    public MissingTypeRegistryPlugin(string typeName, string assemblyName)
 172    {
 173        TypeName = typeName;
 174        AssemblyName = assemblyName;
 175    }
 176
 177    public string TypeName { get; }
 178    public string AssemblyName { get; }
 179}
 180
 181/// <summary>
 182/// Information about an intercepted service (from [Intercept&lt;T&gt;]).
 183/// </summary>
 184internal readonly struct DiscoveredInterceptedService
 185{
 186    public DiscoveredInterceptedService(
 187        string typeName,
 188        string[] interfaceNames,
 189        string assemblyName,
 190        GeneratorLifetime lifetime,
 191        InterceptorDiscoveryHelper.InterceptedMethodInfo[] methods,
 192        string[] allInterceptorTypeNames,
 193        string? sourceFilePath = null)
 194    {
 195        TypeName = typeName;
 196        InterfaceNames = interfaceNames;
 197        AssemblyName = assemblyName;
 198        Lifetime = lifetime;
 199        Methods = methods;
 200        AllInterceptorTypeNames = allInterceptorTypeNames;
 201        SourceFilePath = sourceFilePath;
 202    }
 203
 204    public string TypeName { get; }
 205    public string[] InterfaceNames { get; }
 206    public string AssemblyName { get; }
 207    public GeneratorLifetime Lifetime { get; }
 208    public InterceptorDiscoveryHelper.InterceptedMethodInfo[] Methods { get; }
 209    public string[] AllInterceptorTypeNames { get; }
 210    public string? SourceFilePath { get; }
 211}
 212
 213/// <summary>
 214/// Information about a factory-generated type (from [GenerateFactory]).
 215/// </summary>
 216internal readonly struct DiscoveredFactory
 217{
 218    public DiscoveredFactory(
 219        string typeName,
 220        string[] interfaceNames,
 221        string assemblyName,
 222        int generationMode,
 223        FactoryDiscoveryHelper.FactoryConstructorInfo[] constructors,
 224        string? returnTypeName = null,
 225        string? sourceFilePath = null)
 226    {
 227        TypeName = typeName;
 228        InterfaceNames = interfaceNames;
 229        AssemblyName = assemblyName;
 230        GenerationMode = generationMode;
 231        Constructors = constructors;
 232        ReturnTypeOverride = returnTypeName;
 233        SourceFilePath = sourceFilePath;
 234    }
 235
 236    public string TypeName { get; }
 237    public string[] InterfaceNames { get; }
 238    public string AssemblyName { get; }
 239    /// <summary>Mode flags: 1=Func, 2=Interface, 3=All</summary>
 240    public int GenerationMode { get; }
 241    public FactoryDiscoveryHelper.FactoryConstructorInfo[] Constructors { get; }
 242    /// <summary>
 243    /// If set, the factory Create() and Func return this type instead of the concrete type.
 244    /// Used when [GenerateFactory&lt;T&gt;] is applied.
 245    /// </summary>
 246    public string? ReturnTypeOverride { get; }
 247    public string? SourceFilePath { get; }
 248
 249    public bool GenerateFunc => (GenerationMode & 1) != 0;
 250    public bool GenerateInterface => (GenerationMode & 2) != 0;
 251
 252    /// <summary>Gets the type that factory Create() and Func should return.</summary>
 253    public string ReturnTypeName => ReturnTypeOverride ?? TypeName;
 254
 255    /// <summary>Gets just the type name without namespace (e.g., "MyService" from "global::TestApp.MyService").</summar
 256    public string SimpleTypeName
 257    {
 258        get
 259        {
 260            var parts = TypeName.Split('.');
 261            return parts[parts.Length - 1];
 262        }
 263    }
 264}
 265
 266/// <summary>
 267/// Information about a discovered options type (from [Options]).
 268/// </summary>
 269internal readonly struct DiscoveredOptions
 270{
 271    public DiscoveredOptions(
 272        string typeName,
 273        string sectionName,
 274        string? name,
 275        bool validateOnStart,
 276        string assemblyName,
 277        string? sourceFilePath = null,
 278        OptionsValidatorInfo? validatorMethod = null,
 279        string? validateMethodOverride = null,
 280        string? validatorTypeName = null,
 281        PositionalRecordInfo? positionalRecordInfo = null,
 282        IReadOnlyList<OptionsPropertyInfo>? properties = null)
 283    {
 173284        TypeName = typeName;
 173285        SectionName = sectionName;
 173286        Name = name;
 173287        ValidateOnStart = validateOnStart;
 173288        AssemblyName = assemblyName;
 173289        SourceFilePath = sourceFilePath;
 173290        ValidatorMethod = validatorMethod;
 173291        ValidateMethodOverride = validateMethodOverride;
 173292        ValidatorTypeName = validatorTypeName;
 173293        PositionalRecordInfo = positionalRecordInfo;
 173294        Properties = properties ?? Array.Empty<OptionsPropertyInfo>();
 173295    }
 296
 297    /// <summary>Fully qualified type name of the options class.</summary>
 1006298    public string TypeName { get; }
 299
 300    /// <summary>Configuration section name (e.g., "Database").</summary>
 456301    public string SectionName { get; }
 302
 303    /// <summary>Named options name (e.g., "Primary"), or null for default options.</summary>
 403304    public string? Name { get; }
 305
 306    /// <summary>Whether to validate options on startup.</summary>
 457307    public bool ValidateOnStart { get; }
 308
 165309    public string AssemblyName { get; }
 191310    public string? SourceFilePath { get; }
 311
 312    /// <summary>Information about the validation method (discovered or specified).</summary>
 509313    public OptionsValidatorInfo? ValidatorMethod { get; }
 314
 315    /// <summary>Custom validation method name override from [Options(ValidateMethod = "...")], or null to use conventio
 22316    public string? ValidateMethodOverride { get; }
 317
 318    /// <summary>External validator type name from [Options(Validator = typeof(...))], or null to use options class.</su
 148319    public string? ValidatorTypeName { get; }
 320
 321    /// <summary>Information about positional record primary constructor, if applicable.</summary>
 452322    public PositionalRecordInfo? PositionalRecordInfo { get; }
 323
 324    /// <summary>Bindable properties for AOT code generation.</summary>
 653325    public IReadOnlyList<OptionsPropertyInfo> Properties { get; }
 326
 327    /// <summary>True if this is a named options registration (not default).</summary>
 192328    public bool IsNamed => Name != null;
 329
 330    /// <summary>True if this options type has a custom validator method.</summary>
 429331    public bool HasValidatorMethod => ValidatorMethod != null;
 332
 333    /// <summary>True if an external validator type is specified.</summary>
 138334    public bool HasExternalValidator => ValidatorTypeName != null;
 335
 336    /// <summary>True if this is a positional record that needs a generated parameterless constructor.</summary>
 165337    public bool NeedsGeneratedConstructor => PositionalRecordInfo?.IsPartial == true;
 338
 339    /// <summary>True if this is a non-partial positional record (will emit diagnostic).</summary>
 165340    public bool IsNonPartialPositionalRecord => PositionalRecordInfo != null && !PositionalRecordInfo.Value.IsPartial;
 341
 342    /// <summary>True if this type has any init-only properties (requires factory pattern in AOT).</summary>
 209343    public bool HasInitOnlyProperties => Properties.Any(p => p.HasInitOnlySetter);
 344
 345    /// <summary>True if this is a positional record (uses constructor binding in AOT).</summary>
 87346    public bool IsPositionalRecord => PositionalRecordInfo != null;
 347
 348    /// <summary>True if this type requires factory pattern (Options.Create) instead of Configure delegate in AOT.</summ
 0349    public bool RequiresFactoryPattern => IsPositionalRecord || HasInitOnlyProperties;
 350
 351    /// <summary>True if any property has DataAnnotation validation attributes.</summary>
 1089352    public bool HasDataAnnotations => Properties.Any(p => p.HasDataAnnotations);
 353}
 354
 355/// <summary>
 356/// Information about a positional record's primary constructor parameters.
 357/// </summary>
 358internal readonly struct PositionalRecordInfo
 359{
 360    public PositionalRecordInfo(
 361        string shortTypeName,
 362        string containingNamespace,
 363        bool isPartial,
 364        IReadOnlyList<PositionalRecordParameter> parameters)
 365    {
 366        ShortTypeName = shortTypeName;
 367        ContainingNamespace = containingNamespace;
 368        IsPartial = isPartial;
 369        Parameters = parameters;
 370    }
 371
 372    /// <summary>The simple type name (without namespace).</summary>
 373    public string ShortTypeName { get; }
 374
 375    /// <summary>The containing namespace.</summary>
 376    public string ContainingNamespace { get; }
 377
 378    /// <summary>Whether the record is declared as partial.</summary>
 379    public bool IsPartial { get; }
 380
 381    /// <summary>The primary constructor parameters.</summary>
 382    public IReadOnlyList<PositionalRecordParameter> Parameters { get; }
 383}
 384
 385/// <summary>
 386/// A parameter in a positional record's primary constructor.
 387/// </summary>
 388internal readonly struct PositionalRecordParameter
 389{
 390    public PositionalRecordParameter(string name, string typeName)
 391    {
 392        Name = name;
 393        TypeName = typeName;
 394    }
 395
 396    public string Name { get; }
 397    public string TypeName { get; }
 398}
 399
 400/// <summary>
 401/// Information about a bindable property on an options class (for AOT code generation).
 402/// </summary>
 403internal readonly struct OptionsPropertyInfo
 404{
 405    public OptionsPropertyInfo(
 406        string name,
 407        string typeName,
 408        bool isNullable,
 409        bool hasInitOnlySetter,
 410        bool isEnum = false,
 411        string? enumTypeName = null,
 412        ComplexTypeKind complexTypeKind = ComplexTypeKind.None,
 413        string? elementTypeName = null,
 414        IReadOnlyList<OptionsPropertyInfo>? nestedProperties = null,
 415        IReadOnlyList<DataAnnotationInfo>? dataAnnotations = null)
 416    {
 417        Name = name;
 418        TypeName = typeName;
 419        IsNullable = isNullable;
 420        HasInitOnlySetter = hasInitOnlySetter;
 421        IsEnum = isEnum;
 422        EnumTypeName = enumTypeName;
 423        ComplexTypeKind = complexTypeKind;
 424        ElementTypeName = elementTypeName;
 425        NestedProperties = nestedProperties;
 426        DataAnnotations = dataAnnotations ?? Array.Empty<DataAnnotationInfo>();
 427    }
 428
 429    /// <summary>Property name.</summary>
 430    public string Name { get; }
 431
 432    /// <summary>Fully qualified type name.</summary>
 433    public string TypeName { get; }
 434
 435    /// <summary>True if the property type is nullable.</summary>
 436    public bool IsNullable { get; }
 437
 438    /// <summary>True if the property has an init-only setter.</summary>
 439    public bool HasInitOnlySetter { get; }
 440
 441    /// <summary>True if the property type is an enum.</summary>
 442    public bool IsEnum { get; }
 443
 444    /// <summary>The underlying enum type name (for nullable enums, this is the non-nullable type).</summary>
 445    public string? EnumTypeName { get; }
 446
 447    /// <summary>The kind of complex type (nested object, array, list, dictionary).</summary>
 448    public ComplexTypeKind ComplexTypeKind { get; }
 449
 450    /// <summary>For collections, the element type. For dictionaries, the value type.</summary>
 451    public string? ElementTypeName { get; }
 452
 453    /// <summary>For nested objects and collection element types, the bindable properties.</summary>
 454    public IReadOnlyList<OptionsPropertyInfo>? NestedProperties { get; }
 455
 456    /// <summary>DataAnnotation validation attributes on this property.</summary>
 457    public IReadOnlyList<DataAnnotationInfo> DataAnnotations { get; }
 458
 459    /// <summary>True if this property has any DataAnnotation validation attributes.</summary>
 460    public bool HasDataAnnotations => DataAnnotations.Count > 0;
 461}
 462
 463/// <summary>
 464/// Identifies the kind of complex type for AOT binding generation.
 465/// </summary>
 466internal enum ComplexTypeKind
 467{
 468    /// <summary>Not a complex type (primitive, enum, etc).</summary>
 469    None,
 470    /// <summary>A nested object with properties to bind.</summary>
 471    NestedObject,
 472    /// <summary>An array type (T[]).</summary>
 473    Array,
 474    /// <summary>A list type (List&lt;T&gt;, IList&lt;T&gt;, etc).</summary>
 475    List,
 476    /// <summary>A dictionary type (Dictionary&lt;string, T&gt;).</summary>
 477    Dictionary
 478}
 479
 480/// <summary>
 481/// Identifies the kind of DataAnnotation attribute for source-generated validation.
 482/// </summary>
 483internal enum DataAnnotationKind
 484{
 485    Required,
 486    Range,
 487    StringLength,
 488    MinLength,
 489    MaxLength,
 490    RegularExpression,
 491    EmailAddress,
 492    Phone,
 493    Url,
 494    Unsupported
 495}
 496
 497/// <summary>
 498/// Information about a DataAnnotation attribute on an options property.
 499/// </summary>
 500internal readonly struct DataAnnotationInfo
 501{
 502    public DataAnnotationInfo(
 503        DataAnnotationKind kind,
 504        string? errorMessage = null,
 505        object? minimum = null,
 506        object? maximum = null,
 507        string? pattern = null,
 508        int? minimumLength = null)
 509    {
 510        Kind = kind;
 511        ErrorMessage = errorMessage;
 512        Minimum = minimum;
 513        Maximum = maximum;
 514        Pattern = pattern;
 515        MinimumLength = minimumLength;
 516    }
 517
 518    /// <summary>The kind of DataAnnotation attribute.</summary>
 519    public DataAnnotationKind Kind { get; }
 520
 521    /// <summary>Custom error message if specified.</summary>
 522    public string? ErrorMessage { get; }
 523
 524    /// <summary>Minimum value for Range attribute.</summary>
 525    public object? Minimum { get; }
 526
 527    /// <summary>Maximum value for Range/StringLength/MaxLength attributes.</summary>
 528    public object? Maximum { get; }
 529
 530    /// <summary>Pattern for RegularExpression attribute.</summary>
 531    public string? Pattern { get; }
 532
 533    /// <summary>Minimum length for StringLength/MinLength attributes.</summary>
 534    public int? MinimumLength { get; }
 535}
 536
 537/// <summary>
 538/// Information about a validation method.
 539/// </summary>
 540internal readonly struct OptionsValidatorInfo
 541{
 542    public OptionsValidatorInfo(string methodName, bool isStatic)
 543    {
 544        MethodName = methodName;
 545        IsStatic = isStatic;
 546    }
 547
 548    /// <summary>Name of the validator method.</summary>
 549    public string MethodName { get; }
 550
 551    /// <summary>True if the method is static.</summary>
 552    public bool IsStatic { get; }
 553}
 554
 555/// <summary>
 556/// Information about a discovered Provider (from [Provider] attribute).
 557/// </summary>
 558internal readonly struct DiscoveredProvider
 559{
 560    public DiscoveredProvider(
 561        string typeName,
 562        string assemblyName,
 563        bool isInterface,
 564        bool isPartial,
 565        IReadOnlyList<ProviderPropertyInfo> properties,
 566        string? sourceFilePath = null)
 567    {
 568        TypeName = typeName;
 569        AssemblyName = assemblyName;
 570        IsInterface = isInterface;
 571        IsPartial = isPartial;
 572        Properties = properties;
 573        SourceFilePath = sourceFilePath;
 574    }
 575
 576    /// <summary>Fully qualified type name of the interface or class.</summary>
 577    public string TypeName { get; }
 578
 579    public string AssemblyName { get; }
 580
 581    /// <summary>True if the [Provider] attribute is on an interface.</summary>
 582    public bool IsInterface { get; }
 583
 584    /// <summary>True if the type is a partial class (required for shorthand mode).</summary>
 585    public bool IsPartial { get; }
 586
 587    /// <summary>Properties to generate on the provider.</summary>
 588    public IReadOnlyList<ProviderPropertyInfo> Properties { get; }
 589
 590    public string? SourceFilePath { get; }
 591
 592    /// <summary>Gets simple type name without namespace (e.g., "IOrderProvider" from "global::TestApp.IOrderProvider").
 593    public string SimpleTypeName
 594    {
 595        get
 596        {
 597            var name = TypeName;
 598            var lastDot = name.LastIndexOf('.');
 599            return lastDot >= 0 ? name.Substring(lastDot + 1) : name;
 600        }
 601    }
 602
 603    /// <summary>Gets the implementation class name (removes leading "I" from interface name if present).</summary>
 604    public string ImplementationTypeName
 605    {
 606        get
 607        {
 608            var simple = SimpleTypeName;
 609            if (IsInterface && simple.StartsWith("I") && simple.Length > 1 && char.IsUpper(simple[1]))
 610            {
 611                return simple.Substring(1);
 612            }
 613            return simple;
 614        }
 615    }
 616
 617    /// <summary>Gets the interface name (adds leading "I" to class name if needed).</summary>
 618    public string InterfaceTypeName
 619    {
 620        get
 621        {
 622            var simple = SimpleTypeName;
 623            // For non-interfaces, add "I" prefix unless the name already follows interface naming convention (IXxx)
 624            if (!IsInterface)
 625            {
 626                // Only treat as already having interface prefix if it starts with "I" followed by uppercase letter
 627                if (simple.Length > 1 && simple[0] == 'I' && char.IsUpper(simple[1]))
 628                {
 629                    return simple;
 630                }
 631                return "I" + simple;
 632            }
 633            return simple;
 634        }
 635    }
 636}
 637
 638/// <summary>
 639/// Information about a property on a Provider.
 640/// </summary>
 641internal readonly struct ProviderPropertyInfo
 642{
 643    public ProviderPropertyInfo(
 644        string propertyName,
 645        string serviceTypeName,
 646        ProviderPropertyKind kind)
 647    {
 648        PropertyName = propertyName;
 649        ServiceTypeName = serviceTypeName;
 650        Kind = kind;
 651    }
 652
 653    /// <summary>Property name on the generated provider.</summary>
 654    public string PropertyName { get; }
 655
 656    /// <summary>Fully qualified service type name.</summary>
 657    public string ServiceTypeName { get; }
 658
 659    /// <summary>How this property should be resolved.</summary>
 660    public ProviderPropertyKind Kind { get; }
 661}
 662
 663/// <summary>
 664/// Indicates how a provider property should be resolved.
 665/// </summary>
 666internal enum ProviderPropertyKind
 667{
 668    /// <summary>Required service - uses GetRequiredService&lt;T&gt;().</summary>
 669    Required,
 670
 671    /// <summary>Optional service - uses GetService&lt;T&gt;() and is nullable.</summary>
 672    Optional,
 673
 674    /// <summary>Collection of services - uses GetServices&lt;T&gt;().</summary>
 675    Collection,
 676
 677    /// <summary>Factory for creating new instances.</summary>
 678    Factory
 679}
 680
 681/// <summary>
 682/// Aggregated result of type discovery for an assembly.
 683/// </summary>
 684internal readonly struct DiscoveryResult
 685{
 686    public DiscoveryResult(
 687        IReadOnlyList<DiscoveredType> injectableTypes,
 688        IReadOnlyList<DiscoveredPlugin> pluginTypes,
 689        IReadOnlyList<DiscoveredDecorator> decorators,
 690        IReadOnlyList<InaccessibleType> inaccessibleTypes,
 691        IReadOnlyList<MissingTypeRegistryPlugin> missingTypeRegistryPlugins,
 692        IReadOnlyList<DiscoveredInterceptedService> interceptedServices,
 693        IReadOnlyList<DiscoveredFactory> factories,
 694        IReadOnlyList<DiscoveredOptions> options,
 695        IReadOnlyList<DiscoveredHostedService> hostedServices,
 696        IReadOnlyList<DiscoveredProvider> providers)
 697    {
 698        InjectableTypes = injectableTypes;
 699        PluginTypes = pluginTypes;
 700        Decorators = decorators;
 701        InaccessibleTypes = inaccessibleTypes;
 702        MissingTypeRegistryPlugins = missingTypeRegistryPlugins;
 703        InterceptedServices = interceptedServices;
 704        Factories = factories;
 705        Options = options;
 706        HostedServices = hostedServices;
 707        Providers = providers;
 708    }
 709
 710    public IReadOnlyList<DiscoveredType> InjectableTypes { get; }
 711    public IReadOnlyList<DiscoveredPlugin> PluginTypes { get; }
 712    public IReadOnlyList<DiscoveredDecorator> Decorators { get; }
 713    public IReadOnlyList<InaccessibleType> InaccessibleTypes { get; }
 714    public IReadOnlyList<MissingTypeRegistryPlugin> MissingTypeRegistryPlugins { get; }
 715    public IReadOnlyList<DiscoveredInterceptedService> InterceptedServices { get; }
 716    public IReadOnlyList<DiscoveredFactory> Factories { get; }
 717    public IReadOnlyList<DiscoveredOptions> Options { get; }
 718    public IReadOnlyList<DiscoveredHostedService> HostedServices { get; }
 719    public IReadOnlyList<DiscoveredProvider> Providers { get; }
 720}
 721
 722/// <summary>
 723/// Information parsed from [GenerateTypeRegistry] attribute.
 724/// </summary>
 725internal readonly struct AttributeInfo
 726{
 727    public AttributeInfo(string[]? namespacePrefixes, bool includeSelf)
 728    {
 729        NamespacePrefixes = namespacePrefixes;
 730        IncludeSelf = includeSelf;
 731    }
 732
 733    public string[]? NamespacePrefixes { get; }
 734    public bool IncludeSelf { get; }
 735}