| | | 1 | | using NexusLabs.Needlr.Generators; |
| | | 2 | | |
| | | 3 | | namespace NexusLabs.Needlr.Injection.SourceGen.TypeFilterers; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// A type filterer that uses compile-time generated lifetime information |
| | | 7 | | /// instead of runtime reflection for constructor analysis. |
| | | 8 | | /// </summary> |
| | | 9 | | /// <remarks> |
| | | 10 | | /// <para> |
| | | 11 | | /// When using the generated type registrar with pre-computed lifetimes, |
| | | 12 | | /// this filterer is effectively a no-op since all lifetime decisions are |
| | | 13 | | /// made at compile time. It exists for API compatibility and as a safety |
| | | 14 | | /// fallback that returns false for all checks. |
| | | 15 | | /// </para> |
| | | 16 | | /// <para> |
| | | 17 | | /// For zero-reflection scenarios, use this filterer together with |
| | | 18 | | /// <see cref="TypeRegistrars.GeneratedTypeRegistrar"/> and explicitly |
| | | 19 | | /// provide the type provider function. |
| | | 20 | | /// </para> |
| | | 21 | | /// </remarks> |
| | | 22 | | /// <example> |
| | | 23 | | /// <code> |
| | | 24 | | /// // Zero-reflection configuration: |
| | | 25 | | /// var syringe = new Syringe() |
| | | 26 | | /// .UsingGeneratedTypeRegistrar(TypeRegistry.GetInjectableTypes) |
| | | 27 | | /// .UsingGeneratedTypeFilterer() |
| | | 28 | | /// .UsingGeneratedPluginFactory(TypeRegistry.GetPluginTypes); |
| | | 29 | | /// </code> |
| | | 30 | | /// </example> |
| | | 31 | | [DoNotAutoRegister] |
| | | 32 | | public sealed class GeneratedTypeFilterer : ITypeFilterer |
| | | 33 | | { |
| | | 34 | | private readonly IReadOnlyDictionary<Type, InjectableLifetime>? _lifetimeLookup; |
| | | 35 | | |
| | | 36 | | /// <summary> |
| | | 37 | | /// Initializes a new instance that returns false for all type checks. |
| | | 38 | | /// </summary> |
| | | 39 | | /// <remarks> |
| | | 40 | | /// Use this when all lifetime information is pre-computed by the source generator. |
| | | 41 | | /// The <see cref="TypeRegistrars.GeneratedTypeRegistrar"/> will use pre-computed |
| | | 42 | | /// lifetimes directly and will not need to call this filterer. |
| | | 43 | | /// </remarks> |
| | 14 | 44 | | public GeneratedTypeFilterer() |
| | | 45 | | { |
| | 14 | 46 | | _lifetimeLookup = null; |
| | 14 | 47 | | } |
| | | 48 | | |
| | | 49 | | /// <summary> |
| | | 50 | | /// Initializes a new instance with pre-computed lifetime information. |
| | | 51 | | /// </summary> |
| | | 52 | | /// <param name="typeProvider">A function that returns the injectable types with pre-computed lifetimes.</param> |
| | | 53 | | /// <remarks> |
| | | 54 | | /// This constructor builds a lookup table from the provided type information, |
| | | 55 | | /// enabling reflection-free lifetime checks. |
| | | 56 | | /// </remarks> |
| | 327 | 57 | | public GeneratedTypeFilterer(Func<IReadOnlyList<InjectableTypeInfo>> typeProvider) |
| | | 58 | | { |
| | 327 | 59 | | ArgumentNullException.ThrowIfNull(typeProvider); |
| | | 60 | | |
| | 327 | 61 | | var types = typeProvider(); |
| | 327 | 62 | | var lookup = new Dictionary<Type, InjectableLifetime>(types.Count); |
| | | 63 | | |
| | 100230 | 64 | | foreach (var info in types) |
| | | 65 | | { |
| | 49788 | 66 | | if (info.Lifetime.HasValue) |
| | | 67 | | { |
| | 49788 | 68 | | lookup[info.Type] = info.Lifetime.Value; |
| | | 69 | | } |
| | | 70 | | } |
| | | 71 | | |
| | 327 | 72 | | _lifetimeLookup = lookup; |
| | 327 | 73 | | } |
| | | 74 | | |
| | | 75 | | /// <inheritdoc /> |
| | | 76 | | public bool IsInjectableSingletonType(Type type) |
| | | 77 | | { |
| | 44991 | 78 | | if (_lifetimeLookup is null) |
| | 2244 | 79 | | return false; |
| | | 80 | | |
| | 42747 | 81 | | return _lifetimeLookup.TryGetValue(type, out var lifetime) && |
| | 42747 | 82 | | lifetime == InjectableLifetime.Singleton; |
| | | 83 | | } |
| | | 84 | | |
| | | 85 | | /// <inheritdoc /> |
| | | 86 | | public bool IsInjectableTransientType(Type type) |
| | | 87 | | { |
| | 46403 | 88 | | if (_lifetimeLookup is null) |
| | 2254 | 89 | | return false; |
| | | 90 | | |
| | 44149 | 91 | | return _lifetimeLookup.TryGetValue(type, out var lifetime) && |
| | 44149 | 92 | | lifetime == InjectableLifetime.Transient; |
| | | 93 | | } |
| | | 94 | | |
| | | 95 | | /// <inheritdoc /> |
| | | 96 | | public bool IsInjectableScopedType(Type type) |
| | | 97 | | { |
| | 45616 | 98 | | if (_lifetimeLookup is null) |
| | 2244 | 99 | | return false; |
| | | 100 | | |
| | 43372 | 101 | | return _lifetimeLookup.TryGetValue(type, out var lifetime) && |
| | 43372 | 102 | | lifetime == InjectableLifetime.Scoped; |
| | | 103 | | } |
| | | 104 | | } |