< Summary

Information
Class: NexusLabs.Needlr.Injection.SyringeExtensions
Assembly: NexusLabs.Needlr.Injection
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Injection/SyringeExtensions.cs
Line coverage
86%
Covered lines: 58
Uncovered lines: 9
Coverable lines: 67
Total lines: 388
Line coverage: 86.5%
Branch coverage
100%
Covered branches: 8
Total branches: 8
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Injection/SyringeExtensions.cs

#LineLine coverage
 1using Microsoft.Extensions.Configuration;
 2using Microsoft.Extensions.DependencyInjection;
 3
 4using NexusLabs.Needlr.Injection.AssemblyOrdering;
 5
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8
 9namespace NexusLabs.Needlr.Injection;
 10
 11/// <summary>
 12/// Core extension methods for configuring <see cref="ConfiguredSyringe"/> instances.
 13/// </summary>
 14/// <remarks>
 15/// <para>
 16/// This class contains the core configuration methods that work with any implementation.
 17/// All methods operate on <see cref="ConfiguredSyringe"/>, which is created by calling
 18/// one of the strategy methods on <see cref="Syringe"/>:
 19/// </para>
 20/// <list type="bullet">
 21/// <item><c>new Syringe().UsingSourceGen()</c> - for AOT-compatible source-generated components</item>
 22/// <item><c>new Syringe().UsingReflection()</c> - for runtime reflection-based components</item>
 23/// <item><c>new Syringe().UsingAutoConfiguration()</c> - for automatic fallback</item>
 24/// </list>
 25/// </remarks>
 26public static class SyringeExtensions
 27{
 28    /// <summary>
 29    /// Configures the syringe to use the specified type registrar.
 30    /// </summary>
 31    /// <param name="syringe">The configured syringe to update.</param>
 32    /// <param name="typeRegistrar">The type registrar to use.</param>
 33    /// <returns>A new configured syringe instance.</returns>
 34    public static ConfiguredSyringe UsingTypeRegistrar(
 35        this ConfiguredSyringe syringe,
 36        ITypeRegistrar typeRegistrar)
 37    {
 2938        ArgumentNullException.ThrowIfNull(syringe);
 2939        ArgumentNullException.ThrowIfNull(typeRegistrar);
 40
 2941        return syringe with { TypeRegistrar = typeRegistrar };
 42    }
 43
 44    /// <summary>
 45    /// Configures the syringe to use the specified type filterer.
 46    /// </summary>
 47    /// <param name="syringe">The configured syringe to update.</param>
 48    /// <param name="typeFilterer">The type filterer to use.</param>
 49    /// <returns>A new configured syringe instance.</returns>
 50    public static ConfiguredSyringe UsingTypeFilterer(
 51        this ConfiguredSyringe syringe,
 52        ITypeFilterer typeFilterer)
 53    {
 2754        ArgumentNullException.ThrowIfNull(syringe);
 2755        ArgumentNullException.ThrowIfNull(typeFilterer);
 56
 2757        return syringe with { TypeFilterer = typeFilterer };
 58    }
 59
 60    /// <summary>
 61    /// Configures the syringe to use the specified plugin factory.
 62    /// </summary>
 63    /// <param name="syringe">The configured syringe to update.</param>
 64    /// <param name="pluginFactory">The plugin factory to use.</param>
 65    /// <returns>A new configured syringe instance.</returns>
 66    public static ConfiguredSyringe UsingPluginFactory(
 67        this ConfiguredSyringe syringe,
 68        IPluginFactory pluginFactory)
 69    {
 170        ArgumentNullException.ThrowIfNull(syringe);
 171        ArgumentNullException.ThrowIfNull(pluginFactory);
 72
 173        return syringe with { PluginFactory = pluginFactory };
 74    }
 75
 76    /// <summary>
 77    /// Configures the syringe to use the specified assembly provider.
 78    /// </summary>
 79    /// <param name="syringe">The configured syringe to update.</param>
 80    /// <param name="assemblyProvider">The assembly provider to use.</param>
 81    /// <returns>A new configured syringe instance.</returns>
 82    public static ConfiguredSyringe UsingAssemblyProvider(
 83        this ConfiguredSyringe syringe,
 84        IAssemblyProvider assemblyProvider)
 85    {
 2386        ArgumentNullException.ThrowIfNull(syringe);
 2387        ArgumentNullException.ThrowIfNull(assemblyProvider);
 88
 2389        return syringe with { AssemblyProvider = assemblyProvider };
 90    }
 91
 92    /// <summary>
 93    /// Configures the syringe to use the specified service collection populator factory.
 94    /// </summary>
 95    /// <param name="syringe">The configured syringe to update.</param>
 96    /// <param name="factory">The factory function for creating service collection populators.</param>
 97    /// <returns>A new configured syringe instance.</returns>
 98    public static ConfiguredSyringe UsingServiceCollectionPopulator(
 99        this ConfiguredSyringe syringe,
 100        Func<ITypeRegistrar, ITypeFilterer, IPluginFactory, IServiceCollectionPopulator> factory)
 101    {
 0102        ArgumentNullException.ThrowIfNull(syringe);
 0103        ArgumentNullException.ThrowIfNull(factory);
 104
 0105        return syringe with { ServiceCollectionPopulatorFactory = factory };
 106    }
 107
 108    /// <summary>
 109    /// Configures the syringe to use the specified service provider builder factory.
 110    /// </summary>
 111    /// <param name="syringe">The configured syringe to update.</param>
 112    /// <param name="factory">The factory function for creating service provider builders.</param>
 113    /// <returns>A new configured syringe instance.</returns>
 114    public static ConfiguredSyringe UsingServiceProviderBuilderFactory(
 115        this ConfiguredSyringe syringe,
 116        Func<IServiceCollectionPopulator, IAssemblyProvider, IReadOnlyList<Assembly>, IServiceProviderBuilder> factory)
 117    {
 0118        ArgumentNullException.ThrowIfNull(syringe);
 0119        ArgumentNullException.ThrowIfNull(factory);
 120
 0121        return syringe with { ServiceProviderBuilderFactory = factory };
 122    }
 123
 124    /// <summary>
 125    /// Configures the syringe to use additional assemblies.
 126    /// </summary>
 127    /// <param name="syringe">The configured syringe to update.</param>
 128    /// <param name="additionalAssemblies">The additional assemblies to include.</param>
 129    /// <returns>A new configured syringe instance.</returns>
 130    public static ConfiguredSyringe UsingAdditionalAssemblies(
 131        this ConfiguredSyringe syringe,
 132        IReadOnlyList<Assembly> additionalAssemblies)
 133    {
 13134        ArgumentNullException.ThrowIfNull(syringe);
 13135        ArgumentNullException.ThrowIfNull(additionalAssemblies);
 136
 13137        return syringe with { AdditionalAssemblies = additionalAssemblies };
 138    }
 139
 140    /// <summary>
 141    /// Configures the syringe to use pre-registration callbacks.
 142    /// These callbacks are executed before auto-discovery registration, useful for open generics or base registrations.
 143    /// </summary>
 144    /// <param name="syringe">The configured syringe to update.</param>
 145    /// <param name="callbacks">The callbacks to execute before auto-discovery.</param>
 146    /// <returns>A new configured syringe instance.</returns>
 147    public static ConfiguredSyringe UsingPreRegistrationCallbacks(
 148        this ConfiguredSyringe syringe,
 149        IReadOnlyList<Action<IServiceCollection>> callbacks)
 150    {
 2151        ArgumentNullException.ThrowIfNull(syringe);
 2152        ArgumentNullException.ThrowIfNull(callbacks);
 153
 2154        var result = syringe;
 16155        foreach (var callback in callbacks)
 156        {
 6157            result = result.UsingPreRegistrationCallback(callback);
 158        }
 2159        return result;
 160    }
 161
 162    /// <summary>
 163    /// Configures the syringe to add a single pre-registration callback.
 164    /// This callback is executed before auto-discovery registration, useful for open generics or base registrations.
 165    /// </summary>
 166    /// <param name="syringe">The configured syringe to update.</param>
 167    /// <param name="callback">The callback to execute before auto-discovery.</param>
 168    /// <returns>A new configured syringe instance.</returns>
 169    public static ConfiguredSyringe UsingPreRegistrationCallback(
 170        this ConfiguredSyringe syringe,
 171        Action<IServiceCollection> callback)
 172    {
 18173        ArgumentNullException.ThrowIfNull(syringe);
 18174        ArgumentNullException.ThrowIfNull(callback);
 175
 18176        var existingCallbacks = syringe.PreRegistrationCallbacks ?? [];
 18177        var newCallbacks = new List<Action<IServiceCollection>>(existingCallbacks) { callback };
 178
 18179        return syringe with { PreRegistrationCallbacks = newCallbacks };
 180    }
 181
 182    /// <summary>
 183    /// Configures the syringe to use post-plugin registration callbacks.
 184    /// These callbacks are executed after plugin registration but before the service provider is finalized.
 185    /// </summary>
 186    /// <param name="syringe">The configured syringe to update.</param>
 187    /// <param name="callbacks">The callbacks to execute during service provider building.</param>
 188    /// <returns>A new configured syringe instance.</returns>
 189    public static ConfiguredSyringe UsingPostPluginRegistrationCallbacks(
 190        this ConfiguredSyringe syringe,
 191        IReadOnlyList<Action<IServiceCollection>> callbacks)
 192    {
 0193        ArgumentNullException.ThrowIfNull(syringe);
 0194        ArgumentNullException.ThrowIfNull(callbacks);
 195
 0196        return syringe with { PostPluginRegistrationCallbacks = callbacks };
 197    }
 198
 199    /// <summary>
 200    /// Configures the syringe to add a single post-plugin registration callback.
 201    /// This callback is executed after plugin registration but before the service provider is finalized.
 202    /// </summary>
 203    /// <param name="syringe">The configured syringe to update.</param>
 204    /// <param name="callback">The callback to execute during service provider building.</param>
 205    /// <returns>A new configured syringe instance.</returns>
 206    public static ConfiguredSyringe UsingPostPluginRegistrationCallback(
 207        this ConfiguredSyringe syringe,
 208        Action<IServiceCollection> callback)
 209    {
 149210        ArgumentNullException.ThrowIfNull(syringe);
 149211        ArgumentNullException.ThrowIfNull(callback);
 212
 149213        var existingCallbacks = syringe.PostPluginRegistrationCallbacks ?? [];
 149214        var newCallbacks = new List<Action<IServiceCollection>>(existingCallbacks) { callback };
 215
 149216        return syringe with { PostPluginRegistrationCallbacks = newCallbacks };
 217    }
 218
 219    /// <summary>
 220    /// Configures the syringe to add a decorator for the specified service type.
 221    /// This is a convenience method that adds a post-plugin registration callback to decorate the service.
 222    /// The decorator will preserve the original service's lifetime.
 223    /// Works with both interfaces and class types.
 224    /// </summary>
 225    /// <typeparam name="TService">The service type (interface or class) to decorate.</typeparam>
 226    /// <typeparam name="TDecorator">The decorator type that implements TService.</typeparam>
 227    /// <param name="syringe">The configured syringe to update.</param>
 228    /// <returns>A new configured syringe instance.</returns>
 229    public static ConfiguredSyringe AddDecorator<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Pu
 230        this ConfiguredSyringe syringe)
 231        where TDecorator : class, TService
 232    {
 29233        ArgumentNullException.ThrowIfNull(syringe);
 234
 29235        return syringe.UsingPostPluginRegistrationCallback(services =>
 29236        {
 29237            services.AddDecorator<TService, TDecorator>();
 58238        });
 239    }
 240
 241    /// <summary>
 242    /// Configures assembly ordering using expression-based rules.
 243    /// Assemblies are sorted into tiers based on the first matching rule.
 244    /// Unmatched assemblies are placed last.
 245    /// </summary>
 246    /// <param name="syringe">The configured syringe to update.</param>
 247    /// <param name="configure">Action to configure the ordering rules.</param>
 248    /// <returns>A new configured syringe instance.</returns>
 249    /// <example>
 250    /// <code>
 251    /// new Syringe()
 252    ///     .UsingReflection()  // or .UsingSourceGen()
 253    ///     .OrderAssemblies(order => order
 254    ///         .By(a => a.Name.EndsWith(".Core"))
 255    ///         .ThenBy(a => a.Name.Contains("Services"))
 256    ///         .ThenBy(a => a.Name.Contains("Tests")))
 257    ///     .BuildServiceProvider();
 258    /// </code>
 259    /// </example>
 260    public static ConfiguredSyringe OrderAssemblies(
 261        this ConfiguredSyringe syringe,
 262        Action<AssemblyOrderBuilder> configure)
 263    {
 21264        ArgumentNullException.ThrowIfNull(syringe);
 21265        ArgumentNullException.ThrowIfNull(configure);
 266
 20267        var builder = new AssemblyOrderBuilder();
 20268        configure(builder);
 20269        return syringe with { AssemblyOrder = builder };
 270    }
 271
 272    /// <summary>
 273    /// Configures assembly ordering using a pre-built order builder.
 274    /// </summary>
 275    /// <param name="syringe">The configured syringe to update.</param>
 276    /// <param name="orderBuilder">The pre-configured order builder.</param>
 277    /// <returns>A new configured syringe instance.</returns>
 278    /// <example>
 279    /// <code>
 280    /// new Syringe()
 281    ///     .UsingSourceGen()
 282    ///     .OrderAssemblies(AssemblyOrder.LibTestEntry())
 283    ///     .BuildServiceProvider();
 284    /// </code>
 285    /// </example>
 286    public static ConfiguredSyringe OrderAssemblies(
 287        this ConfiguredSyringe syringe,
 288        AssemblyOrderBuilder orderBuilder)
 289    {
 10290        ArgumentNullException.ThrowIfNull(syringe);
 10291        ArgumentNullException.ThrowIfNull(orderBuilder);
 292
 9293        return syringe with { AssemblyOrder = orderBuilder };
 294    }
 295
 296    /// <summary>
 297    /// Configures assembly ordering: libraries first, then executables, tests last.
 298    /// </summary>
 299    /// <param name="syringe">The configured syringe to update.</param>
 300    /// <returns>A new configured syringe instance.</returns>
 301    public static ConfiguredSyringe UseLibTestEntryOrdering(this ConfiguredSyringe syringe)
 302    {
 4303        ArgumentNullException.ThrowIfNull(syringe);
 4304        return syringe.OrderAssemblies(AssemblyOrder.LibTestEntry());
 305    }
 306
 307    /// <summary>
 308    /// Configures assembly ordering: non-test assemblies first, tests last.
 309    /// </summary>
 310    /// <param name="syringe">The configured syringe to update.</param>
 311    /// <returns>A new configured syringe instance.</returns>
 312    public static ConfiguredSyringe UseTestsLastOrdering(this ConfiguredSyringe syringe)
 313    {
 3314        ArgumentNullException.ThrowIfNull(syringe);
 3315        return syringe.OrderAssemblies(AssemblyOrder.TestsLast());
 316    }
 317
 318    /// <summary>
 319    /// Configures verification options for the syringe.
 320    /// Verification runs automatically during <see cref="ConfiguredSyringe.BuildServiceProvider"/>.
 321    /// </summary>
 322    /// <param name="syringe">The configured syringe to update.</param>
 323    /// <param name="options">The verification options to use.</param>
 324    /// <returns>A new configured syringe instance.</returns>
 325    /// <example>
 326    /// <code>
 327    /// // Strict mode - throw on any issue
 328    /// new Syringe()
 329    ///     .UsingSourceGen()
 330    ///     .WithVerification(VerificationOptions.Strict)
 331    ///     .BuildServiceProvider();
 332    ///
 333    /// // Disable verification
 334    /// new Syringe()
 335    ///     .UsingSourceGen()
 336    ///     .WithVerification(VerificationOptions.Disabled)
 337    ///     .BuildServiceProvider();
 338    ///
 339    /// // Custom configuration
 340    /// new Syringe()
 341    ///     .UsingSourceGen()
 342    ///     .WithVerification(new VerificationOptions
 343    ///     {
 344    ///         LifetimeMismatchBehavior = VerificationBehavior.Throw,
 345    ///         IssueReporter = issue => logger.LogWarning(issue.Message)
 346    ///     })
 347    ///     .BuildServiceProvider();
 348    /// </code>
 349    /// </example>
 350    public static ConfiguredSyringe WithVerification(
 351        this ConfiguredSyringe syringe,
 352        VerificationOptions options)
 353    {
 5354        ArgumentNullException.ThrowIfNull(syringe);
 5355        ArgumentNullException.ThrowIfNull(options);
 356
 5357        return syringe with { VerificationOptions = options };
 358    }
 359
 360    /// <summary>
 361    /// Configures verification options using a builder action.
 362    /// </summary>
 363    /// <param name="syringe">The configured syringe to update.</param>
 364    /// <param name="configure">An action to configure the verification options.</param>
 365    /// <returns>A new configured syringe instance.</returns>
 366    public static ConfiguredSyringe WithVerification(
 367        this ConfiguredSyringe syringe,
 368        Action<VerificationOptionsBuilder> configure)
 369    {
 2370        ArgumentNullException.ThrowIfNull(syringe);
 2371        ArgumentNullException.ThrowIfNull(configure);
 372
 2373        var builder = new VerificationOptionsBuilder();
 2374        configure(builder);
 2375        return syringe with { VerificationOptions = builder.Build() };
 376    }
 377
 378    /// <summary>
 379    /// Builds a service provider with default configuration.
 380    /// </summary>
 381    /// <param name="syringe">The configured syringe to build from.</param>
 382    /// <returns>The configured <see cref="IServiceProvider"/>.</returns>
 383    public static IServiceProvider BuildServiceProvider(this ConfiguredSyringe syringe)
 384    {
 421385        ArgumentNullException.ThrowIfNull(syringe);
 421386        return syringe.BuildServiceProvider(new Microsoft.Extensions.Configuration.ConfigurationBuilder().Build());
 387    }
 388}

Methods/Properties

UsingTypeRegistrar(NexusLabs.Needlr.Injection.ConfiguredSyringe,NexusLabs.Needlr.Injection.ITypeRegistrar)
UsingTypeFilterer(NexusLabs.Needlr.Injection.ConfiguredSyringe,NexusLabs.Needlr.Injection.ITypeFilterer)
UsingPluginFactory(NexusLabs.Needlr.Injection.ConfiguredSyringe,NexusLabs.Needlr.IPluginFactory)
UsingAssemblyProvider(NexusLabs.Needlr.Injection.ConfiguredSyringe,NexusLabs.Needlr.Injection.IAssemblyProvider)
UsingServiceCollectionPopulator(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Func`4<NexusLabs.Needlr.Injection.ITypeRegistrar,NexusLabs.Needlr.Injection.ITypeFilterer,NexusLabs.Needlr.IPluginFactory,NexusLabs.Needlr.Injection.IServiceCollectionPopulator>)
UsingServiceProviderBuilderFactory(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Func`4<NexusLabs.Needlr.Injection.IServiceCollectionPopulator,NexusLabs.Needlr.Injection.IAssemblyProvider,System.Collections.Generic.IReadOnlyList`1<System.Reflection.Assembly>,NexusLabs.Needlr.Injection.IServiceProviderBuilder>)
UsingAdditionalAssemblies(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Collections.Generic.IReadOnlyList`1<System.Reflection.Assembly>)
UsingPreRegistrationCallbacks(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Collections.Generic.IReadOnlyList`1<System.Action`1<Microsoft.Extensions.DependencyInjection.IServiceCollection>>)
UsingPreRegistrationCallback(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Action`1<Microsoft.Extensions.DependencyInjection.IServiceCollection>)
UsingPostPluginRegistrationCallbacks(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Collections.Generic.IReadOnlyList`1<System.Action`1<Microsoft.Extensions.DependencyInjection.IServiceCollection>>)
UsingPostPluginRegistrationCallback(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Action`1<Microsoft.Extensions.DependencyInjection.IServiceCollection>)
AddDecorator(NexusLabs.Needlr.Injection.ConfiguredSyringe)
OrderAssemblies(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Action`1<NexusLabs.Needlr.Injection.AssemblyOrdering.AssemblyOrderBuilder>)
OrderAssemblies(NexusLabs.Needlr.Injection.ConfiguredSyringe,NexusLabs.Needlr.Injection.AssemblyOrdering.AssemblyOrderBuilder)
UseLibTestEntryOrdering(NexusLabs.Needlr.Injection.ConfiguredSyringe)
UseTestsLastOrdering(NexusLabs.Needlr.Injection.ConfiguredSyringe)
WithVerification(NexusLabs.Needlr.Injection.ConfiguredSyringe,NexusLabs.Needlr.VerificationOptions)
WithVerification(NexusLabs.Needlr.Injection.ConfiguredSyringe,System.Action`1<NexusLabs.Needlr.VerificationOptionsBuilder>)
BuildServiceProvider(NexusLabs.Needlr.Injection.ConfiguredSyringe)