< Summary

Information
Class: NexusLabs.Needlr.Generators.PluginTypeInfo
Assembly: NexusLabs.Needlr.Generators.Attributes
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators.Attributes/InjectableTypeInfo.cs
Line coverage
79%
Covered lines: 19
Uncovered lines: 5
Coverable lines: 24
Total lines: 274
Line coverage: 79.1%
Branch coverage
50%
Covered branches: 4
Total branches: 8
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_PluginType()100%11100%
get_PluginInterfaces()100%210%
get_Factory()100%11100%
get_Attributes()100%11100%
get_Order()100%11100%
HasAttribute()100%44100%
HasAttribute(...)0%2040%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators.Attributes/InjectableTypeInfo.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3
 4namespace NexusLabs.Needlr.Generators;
 5
 6/// <summary>
 7/// Specifies the lifetime of a service in the dependency injection container.
 8/// </summary>
 9/// <remarks>
 10/// This enum mirrors <c>Microsoft.Extensions.DependencyInjection.ServiceLifetime</c>
 11/// to avoid adding a dependency on that package in the attributes assembly.
 12/// </remarks>
 13public enum InjectableLifetime
 14{
 15    /// <summary>
 16    /// A single instance is created and shared across all requests.
 17    /// </summary>
 18    Singleton = 0,
 19
 20    /// <summary>
 21    /// A new instance is created for each scope (e.g., each HTTP request).
 22    /// </summary>
 23    Scoped = 1,
 24
 25    /// <summary>
 26    /// A new instance is created each time the service is requested.
 27    /// </summary>
 28    Transient = 2
 29}
 30
 31/// <summary>
 32/// Represents metadata about an injectable type discovered at compile time.
 33/// </summary>
 34/// <remarks>
 35/// This struct is used by the generated TypeRegistry to provide type
 36/// information without requiring reflection at runtime.
 37/// </remarks>
 38public readonly struct InjectableTypeInfo
 39{
 40    /// <summary>
 41    /// Initializes a new instance of <see cref="InjectableTypeInfo"/>.
 42    /// </summary>
 43    /// <param name="type">The concrete implementation type.</param>
 44    /// <param name="interfaces">The interfaces implemented by the type that should be registered.</param>
 45    public InjectableTypeInfo(Type type, IReadOnlyList<Type> interfaces)
 46        : this(type, interfaces, null, null)
 47    {
 48    }
 49
 50    /// <summary>
 51    /// Initializes a new instance of <see cref="InjectableTypeInfo"/>.
 52    /// </summary>
 53    /// <param name="type">The concrete implementation type.</param>
 54    /// <param name="interfaces">The interfaces implemented by the type that should be registered.</param>
 55    /// <param name="lifetime">The pre-computed service lifetime, or null if not determined.</param>
 56    public InjectableTypeInfo(Type type, IReadOnlyList<Type> interfaces, InjectableLifetime? lifetime)
 57        : this(type, interfaces, lifetime, null)
 58    {
 59    }
 60
 61    /// <summary>
 62    /// Initializes a new instance of <see cref="InjectableTypeInfo"/>.
 63    /// </summary>
 64    /// <param name="type">The concrete implementation type.</param>
 65    /// <param name="interfaces">The interfaces implemented by the type that should be registered.</param>
 66    /// <param name="lifetime">The pre-computed service lifetime, or null if not determined.</param>
 67    /// <param name="factory">
 68    /// A factory delegate that creates an instance of the type using the service provider.
 69    /// When provided, enables AOT-compatible instantiation without runtime reflection.
 70    /// </param>
 71    public InjectableTypeInfo(Type type, IReadOnlyList<Type> interfaces, InjectableLifetime? lifetime, Func<IServiceProv
 72        : this(type, interfaces, lifetime, factory, Array.Empty<string>())
 73    {
 74    }
 75
 76    /// <summary>
 77    /// Initializes a new instance of <see cref="InjectableTypeInfo"/> with keyed service support.
 78    /// </summary>
 79    /// <param name="type">The concrete implementation type.</param>
 80    /// <param name="interfaces">The interfaces implemented by the type that should be registered.</param>
 81    /// <param name="lifetime">The pre-computed service lifetime, or null if not determined.</param>
 82    /// <param name="factory">
 83    /// A factory delegate that creates an instance of the type using the service provider.
 84    /// When provided, enables AOT-compatible instantiation without runtime reflection.
 85    /// </param>
 86    /// <param name="serviceKeys">
 87    /// The service keys from [Keyed] attributes. When provided, the type will be registered
 88    /// as a keyed service in addition to its normal registration.
 89    /// </param>
 90    public InjectableTypeInfo(Type type, IReadOnlyList<Type> interfaces, InjectableLifetime? lifetime, Func<IServiceProv
 91    {
 92        Type = type;
 93        Interfaces = interfaces;
 94        Lifetime = lifetime;
 95        Factory = factory;
 96        ServiceKeys = serviceKeys;
 97    }
 98
 99    /// <summary>
 100    /// Gets the concrete implementation type.
 101    /// </summary>
 102    public Type Type { get; }
 103
 104    /// <summary>
 105    /// Gets the interfaces implemented by the type that should be registered.
 106    /// </summary>
 107    public IReadOnlyList<Type> Interfaces { get; }
 108
 109    /// <summary>
 110    /// Gets the pre-computed service lifetime for this type, or null if
 111    /// the lifetime should be determined at runtime by an <c>ITypeFilterer</c>.
 112    /// </summary>
 113    /// <remarks>
 114    /// When this value is set, the type registrar can skip runtime reflection
 115    /// for constructor analysis, improving startup performance and enabling
 116    /// AOT compilation scenarios.
 117    /// </remarks>
 118    public InjectableLifetime? Lifetime { get; }
 119
 120    /// <summary>
 121    /// Gets a factory delegate that creates an instance of the type using the service provider.
 122    /// </summary>
 123    /// <remarks>
 124    /// <para>
 125    /// When this delegate is provided, the type registrar can use it to create instances
 126    /// without relying on <c>Activator.CreateInstance</c> or reflection-based constructor
 127    /// invocation. This is essential for NativeAOT scenarios where reflection may be disabled.
 128    /// </para>
 129    /// <para>
 130    /// The factory receives an <see cref="IServiceProvider"/> and should resolve all
 131    /// constructor dependencies from it before creating the instance.
 132    /// </para>
 133    /// </remarks>
 134    public Func<IServiceProvider, object>? Factory { get; }
 135
 136    /// <summary>
 137    /// Gets the service keys from [Keyed] attributes on this type.
 138    /// </summary>
 139    /// <remarks>
 140    /// <para>
 141    /// When service keys are present, the type will be registered as a keyed service
 142    /// in addition to its normal registration. Consumers can then resolve the specific
 143    /// implementation using <c>[FromKeyedServices("key")]</c> on constructor parameters.
 144    /// </para>
 145    /// <para>
 146    /// A type can have multiple keys, resulting in multiple keyed registrations for
 147    /// the same implementation.
 148    /// </para>
 149    /// </remarks>
 150    public IReadOnlyList<string> ServiceKeys { get; }
 151}
 152
 153/// <summary>
 154/// Represents metadata about a plugin type discovered at compile time.
 155/// </summary>
 156/// <remarks>
 157/// This struct is used by the generated TypeRegistry to provide plugin
 158/// factory methods without requiring reflection or Activator.CreateInstance at runtime.
 159/// </remarks>
 160public readonly struct PluginTypeInfo
 161{
 162    /// <summary>
 163    /// Initializes a new instance of <see cref="PluginTypeInfo"/>.
 164    /// </summary>
 165    /// <param name="pluginType">The concrete plugin type.</param>
 166    /// <param name="pluginInterfaces">The plugin interfaces implemented by the type.</param>
 167    /// <param name="factory">A factory delegate that creates an instance of the plugin.</param>
 168    public PluginTypeInfo(Type pluginType, IReadOnlyList<Type> pluginInterfaces, Func<object> factory)
 23169        : this(pluginType, pluginInterfaces, factory, Array.Empty<Type>(), 0)
 170    {
 23171    }
 172
 173    /// <summary>
 174    /// Initializes a new instance of <see cref="PluginTypeInfo"/>.
 175    /// </summary>
 176    /// <param name="pluginType">The concrete plugin type.</param>
 177    /// <param name="pluginInterfaces">The plugin interfaces implemented by the type.</param>
 178    /// <param name="factory">A factory delegate that creates an instance of the plugin.</param>
 179    /// <param name="attributes">The attribute types applied to the plugin type.</param>
 180    public PluginTypeInfo(Type pluginType, IReadOnlyList<Type> pluginInterfaces, Func<object> factory, IReadOnlyList<Typ
 22181        : this(pluginType, pluginInterfaces, factory, attributes, 0)
 182    {
 22183    }
 184
 185    /// <summary>
 186    /// Initializes a new instance of <see cref="PluginTypeInfo"/> with execution order.
 187    /// </summary>
 188    /// <param name="pluginType">The concrete plugin type.</param>
 189    /// <param name="pluginInterfaces">The plugin interfaces implemented by the type.</param>
 190    /// <param name="factory">A factory delegate that creates an instance of the plugin.</param>
 191    /// <param name="attributes">The attribute types applied to the plugin type.</param>
 192    /// <param name="order">The execution order. Lower values execute first.</param>
 193    public PluginTypeInfo(Type pluginType, IReadOnlyList<Type> pluginInterfaces, Func<object> factory, IReadOnlyList<Typ
 194    {
 150195        PluginType = pluginType;
 150196        PluginInterfaces = pluginInterfaces;
 150197        Factory = factory;
 150198        Attributes = attributes;
 150199        Order = order;
 150200    }
 201
 202    /// <summary>
 203    /// Gets the concrete plugin type.
 204    /// </summary>
 180052205    public Type PluginType { get; }
 206
 207    /// <summary>
 208    /// Gets the plugin interfaces implemented by the type.
 209    /// </summary>
 0210    public IReadOnlyList<Type> PluginInterfaces { get; }
 211
 212    /// <summary>
 213    /// Gets a factory delegate that creates an instance of the plugin
 214    /// without using Activator.CreateInstance.
 215    /// </summary>
 1922216    public Func<object> Factory { get; }
 217
 218    /// <summary>
 219    /// Gets the attribute types applied to the plugin type.
 220    /// </summary>
 221    /// <remarks>
 222    /// This enables attribute-based plugin filtering without requiring
 223    /// reflection via <c>GetCustomAttribute</c> at runtime.
 224    /// </remarks>
 444225    public IReadOnlyList<Type> Attributes { get; }
 226
 227    /// <summary>
 228    /// Gets the execution order for this plugin. Lower values execute first.
 229    /// </summary>
 230    /// <remarks>
 231    /// <para>
 232    /// Plugins with lower order values are executed before those with higher values.
 233    /// The default order is 0. Use negative values for plugins that must run early
 234    /// (e.g., infrastructure setup) and positive values for plugins that must run
 235    /// late (e.g., validation, cleanup).
 236    /// </para>
 237    /// <para>
 238    /// When multiple plugins have the same order, they are sorted alphabetically
 239    /// by their fully qualified type name for deterministic execution.
 240    /// </para>
 241    /// </remarks>
 46032242    public int Order { get; }
 243
 244    /// <summary>
 245    /// Checks if the plugin has an attribute of the specified type.
 246    /// </summary>
 247    /// <typeparam name="TAttribute">The attribute type to check for.</typeparam>
 248    /// <returns>True if the plugin has the attribute; otherwise, false.</returns>
 249    public bool HasAttribute<TAttribute>() where TAttribute : Attribute
 250    {
 256251        var attributeType = typeof(TAttribute);
 666252        for (int i = 0; i < Attributes.Count; i++)
 253        {
 111254            if (attributeType.IsAssignableFrom(Attributes[i]))
 34255                return true;
 256        }
 222257        return false;
 258    }
 259
 260    /// <summary>
 261    /// Checks if the plugin has an attribute of the specified type.
 262    /// </summary>
 263    /// <param name="attributeType">The attribute type to check for.</param>
 264    /// <returns>True if the plugin has the attribute; otherwise, false.</returns>
 265    public bool HasAttribute(Type attributeType)
 266    {
 0267        for (int i = 0; i < Attributes.Count; i++)
 268        {
 0269            if (attributeType.IsAssignableFrom(Attributes[i]))
 0270                return true;
 271        }
 0272        return false;
 273    }
 274}