< Summary

Information
Class: NexusLabs.Needlr.Generators.RegisterClosedOverImplementationsOfAttribute
Assembly: NexusLabs.Needlr.Generators.Attributes
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators.Attributes/RegisterClosedOverImplementationsOfAttribute.cs
Line coverage
83%
Covered lines: 5
Uncovered lines: 1
Coverable lines: 6
Total lines: 96
Line coverage: 83.3%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_SourceOpenGenericInterface()100%210%
get_As()100%11100%
get_Lifetime()100%11100%

File(s)

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

#LineLine coverage
 1using System;
 2
 3namespace NexusLabs.Needlr.Generators;
 4
 5/// <summary>
 6/// Marks an open generic <em>composition</em> class so that the source generator registers a closed
 7/// instance of it for <strong>each</strong> discovered concrete implementation of a designated
 8/// open generic interface, exposed as a designated non-generic (or less-generic) facade service type.
 9/// </summary>
 10/// <remarks>
 11/// <para>
 12/// This is a source-generation only feature. It requires the <c>NexusLabs.Needlr.Generators</c>
 13/// package and has no effect when using reflection-based registration.
 14/// </para>
 15/// <para>
 16/// Use this attribute to keep a "compose-and-expose" pattern on the source-generation path instead of
 17/// hand-maintaining one registration line per type argument (which silently drifts) or falling back to
 18/// runtime reflection (which is AOT/trimming hostile). For every concrete closed implementation of the
 19/// designated open generic interface that the generator discovers, it closes this composition class over
 20/// the <em>same</em> type argument(s) and registers it as the service type named by <see cref="As"/>,
 21/// resolving the composition's constructor dependencies from the service provider.
 22/// </para>
 23/// <para>
 24/// The annotated class must:
 25/// <list type="bullet">
 26/// <item><description>Be an open generic class whose type-parameter arity matches the open generic interface.</descript
 27/// <item><description>Implement the service type specified by <see cref="As"/>.</description></item>
 28/// <item><description>Have a single public constructor whose parameters are all resolvable from the service provider.</
 29/// </list>
 30/// </para>
 31/// <para>
 32/// A discovered type argument that does not satisfy the composition class's generic constraints is
 33/// skipped with a build diagnostic (<c>NDLRGEN038</c>) rather than producing a runtime failure.
 34/// </para>
 35/// </remarks>
 36/// <example>
 37/// <code>
 38/// // Auto-discovered today: concrete, closed, unattributed.
 39/// public interface IFooDefinition&lt;TData&gt; where TData : class
 40/// {
 41///     string Discriminator { get; }
 42/// }
 43///
 44/// public sealed class AlphaFoo : IFooDefinition&lt;AlphaData&gt; { /* ... */ }
 45/// public sealed class BetaFoo  : IFooDefinition&lt;BetaData&gt;  { /* ... */ }
 46///
 47/// // Non-generic facade consumed as IEnumerable&lt;IFoo&gt;.
 48/// public interface IFoo { string Discriminator { get; } }
 49///
 50/// // Reusable composition closed per discovered TData and exposed as IFoo.
 51/// [RegisterClosedOverImplementationsOf(typeof(IFooDefinition&lt;&gt;), As = typeof(IFoo))]
 52/// public sealed class FooCore&lt;TData&gt; : IFoo where TData : class
 53/// {
 54///     public FooCore(IFooDefinition&lt;TData&gt; definition, IFooStore&lt;TData&gt; store) { /* ... */ }
 55///     public string Discriminator =&gt; /* delegates to definition */ "";
 56/// }
 57///
 58/// // Generator emits, per discovered implementation:
 59/// // services.AddSingleton&lt;IFoo&gt;(sp =&gt; new FooCore&lt;AlphaData&gt;(
 60/// //     sp.GetRequiredService&lt;IFooDefinition&lt;AlphaData&gt;&gt;(),
 61/// //     sp.GetRequiredService&lt;IFooStore&lt;AlphaData&gt;&gt;()));
 62/// // services.AddSingleton&lt;IFoo&gt;(sp =&gt; new FooCore&lt;BetaData&gt;( ... ));
 63/// </code>
 64/// </example>
 65[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
 66public sealed class RegisterClosedOverImplementationsOfAttribute : Attribute
 67{
 68    /// <summary>
 69    /// Initializes a new instance of the <see cref="RegisterClosedOverImplementationsOfAttribute"/> class.
 70    /// </summary>
 71    /// <param name="sourceOpenGenericInterface">
 72    /// The open generic interface whose concrete closed implementations drive registration
 73    /// (e.g., <c>typeof(IFooDefinition&lt;&gt;)</c>). Must be an open generic interface.
 74    /// </param>
 123075    public RegisterClosedOverImplementationsOfAttribute(Type sourceOpenGenericInterface)
 76    {
 123077        SourceOpenGenericInterface = sourceOpenGenericInterface;
 123078    }
 79
 80    /// <summary>
 81    /// Gets the open generic interface whose concrete closed implementations drive registration.
 82    /// </summary>
 083    public Type SourceOpenGenericInterface { get; }
 84
 85    /// <summary>
 86    /// Gets or sets the service type that each closed composition is registered as
 87    /// (e.g., <c>typeof(IFoo)</c>). Must be a type implemented by the annotated composition class.
 88    /// </summary>
 123089    public Type? As { get; set; }
 90
 91    /// <summary>
 92    /// Gets or sets the lifetime each closed composition registration is given.
 93    /// Defaults to <see cref="InjectableLifetime.Singleton"/>.
 94    /// </summary>
 82095    public InjectableLifetime Lifetime { get; set; } = InjectableLifetime.Singleton;
 96}