diff --git a/src/Pipelines.Generator/Extensions/AttributeDataExtensions.cs b/src/Pipelines.Generator/Extensions/AttributeDataExtensions.cs
new file mode 100644
index 0000000..63eb543
--- /dev/null
+++ b/src/Pipelines.Generator/Extensions/AttributeDataExtensions.cs
@@ -0,0 +1,24 @@
+namespace Microsoft.CodeAnalysis;
+
+///
+/// Provides extension methods for to identify pipeline-related attributes.
+///
+internal static class AttributeDataExtensions
+{
+ ///
+ /// Extension methods for an instance.
+ ///
+ /// The attribute data to inspect.
+ extension(AttributeData? attribute)
+ {
+ ///
+ /// Determines whether the attribute represents a Pipelines.IgnoreAttribute with no constructor arguments.
+ ///
+ public bool IsIgnoreAttribute() => attribute is
+ {
+ AttributeClass.ContainingAssembly.Name: "Pipelines",
+ AttributeClass.Name: "IgnoreAttribute",
+ ConstructorArguments.IsDefaultOrEmpty: true,
+ };
+ }
+}
diff --git a/src/Pipelines.Generator/Extensions/SourceBuilderExtensions.cs b/src/Pipelines.Generator/Extensions/SourceBuilderExtensions.cs
index d3dd51c..5f93dff 100644
--- a/src/Pipelines.Generator/Extensions/SourceBuilderExtensions.cs
+++ b/src/Pipelines.Generator/Extensions/SourceBuilderExtensions.cs
@@ -55,7 +55,7 @@ public void NullableDisable()
}
///
- /// Writes a line with the generator name and version.
+ /// Writes a using statement for the namespace.
///
public void UsingSystemCodeDomCompiler()
{
diff --git a/src/Pipelines.Generator/Generators/HandlesrRegistration/HandlesrRegistrationGenerator.cs b/src/Pipelines.Generator/Generators/HandlesrRegistration/HandlesrRegistrationGenerator.cs
index fad97cb..638200d 100644
--- a/src/Pipelines.Generator/Generators/HandlesrRegistration/HandlesrRegistrationGenerator.cs
+++ b/src/Pipelines.Generator/Generators/HandlesrRegistration/HandlesrRegistrationGenerator.cs
@@ -14,12 +14,14 @@ public static void Initialize(PipelinesGeneratorContext ctx)
.SyntaxProvider
.CreateSyntaxProvider(Predicate, (context, ct) => Transform(context.Node, context.SemanticModel, ct))
.SelectMany((x, ct) => x)
- .Collect()
- .Combine(features);
+ .Collect();
- context.RegisterSourceOutput(handlers, static (spc, ctx) =>
+ var toGenerate = features
+ .Combine(handlers);
+
+ context.RegisterSourceOutput(toGenerate, static (spc, ctx) =>
{
- var (handlers, features) = ctx;
+ var (features, handlers) = ctx;
if (features is not { HasDependencyInjection: true, DisableHandlerRegistration: false })
return;
@@ -50,10 +52,9 @@ private static IEnumerable Transform(SyntaxNode node, Seman
if (IRequestHandler(interfaceSymbol) ||
IStreamRequestHandler(interfaceSymbol))
{
- yield return new HandlerRegistration(node, symbol, interfaceSymbol);
+ yield return new HandlerRegistration(symbol, interfaceSymbol);
}
}
- //return null;
static bool ValidHandler([NotNullWhen(true)] INamedTypeSymbol? handler) => handler is
{
@@ -66,7 +67,7 @@ static bool ValidHandler([NotNullWhen(true)] INamedTypeSymbol? handler) => handl
static bool ValidTypeArgument(ITypeSymbol typeSymbol) => typeSymbol switch
{
- // todo: expand failure criteria
+ // todo: expand failure criteria and add diagnostics
INamedTypeSymbol named => named is
{
IsAbstract: false,
@@ -101,7 +102,9 @@ static bool IStreamRequestHandler(INamedTypeSymbol handler)
}
}
- private static void GenerateSourceOutput(SourceProductionContext spc, ImmutableArray handlersToGenerate)
+ private static void GenerateSourceOutput(
+ in SourceProductionContext spc,
+ in ImmutableArray handlers)
{
// todo: diagnostics for duplicate handlers
//var descriptor = new DiagnosticDescriptor(
@@ -130,9 +133,13 @@ private static void GenerateSourceOutput(SourceProductionContext spc, ImmutableA
{
using (sb.Block(sb, $"public static {sb.IServiceCollection} AddHandlers(this {sb.IServiceCollection} services)"))
{
- foreach (var handlerToGenerate in handlersToGenerate)
+ if (!handlers.IsDefaultOrEmpty)
{
- handlerToGenerate.ServiceRegistration(sb);
+ foreach (var handler in handlers)
+ {
+ handler.ServiceRegistration(sb);
+ }
+ sb.Line();
}
sb.Line("return services;");
}
diff --git a/src/Pipelines.Generator/Generators/HandlesrRegistration/Models/HandlerRegistration.cs b/src/Pipelines.Generator/Generators/HandlesrRegistration/Models/HandlerRegistration.cs
index f47ceb0..604d7b7 100644
--- a/src/Pipelines.Generator/Generators/HandlesrRegistration/Models/HandlerRegistration.cs
+++ b/src/Pipelines.Generator/Generators/HandlesrRegistration/Models/HandlerRegistration.cs
@@ -2,19 +2,36 @@
internal readonly record struct HandlerRegistration
{
+ private readonly bool Disabled;
+
public readonly string Handler;
+
public readonly string Interface;
- public readonly SyntaxNode Node;
- public HandlerRegistration(SyntaxNode node, INamedTypeSymbol classSymbol, INamedTypeSymbol interfaceSymbol)
+ public HandlerRegistration(INamedTypeSymbol classSymbol, INamedTypeSymbol interfaceSymbol)
{
+ var attributes = classSymbol.GetAttributes();
+
+ Disabled = attributes.Any(x => x.IsIgnoreAttribute());
Handler = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
Interface = interfaceSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
- Node = node;
}
public void ServiceRegistration(SourceBuilder sb)
{
+ if (Disabled) return;
+
sb.Line(sb, $"services.AddScoped<{Interface}, {Handler}>();");
}
+
+ public bool Equals(HandlerRegistration other)
+ {
+ return other.Handler == Handler
+ && other.Interface == Interface;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Handler, Interface);
+ }
}
diff --git a/src/Pipelines.Generator/Primitives/EquatableArray.cs b/src/Pipelines.Generator/Primitives/EquatableArray.cs
new file mode 100644
index 0000000..c8da60a
--- /dev/null
+++ b/src/Pipelines.Generator/Primitives/EquatableArray.cs
@@ -0,0 +1,134 @@
+using System.Runtime.CompilerServices;
+
+namespace System.Collections.Generic;
+
+///
+/// Provides extension methods for creating instances.
+///
+internal static class EquatableArray
+{
+ ///
+ /// Converts an to an .
+ ///
+ /// The type of elements in the collection.
+ /// The collection of values to convert.
+ /// An containing the values from the input collection.
+ public static EquatableArray ToEquatableArray(this IEnumerable values) where T : IEquatable
+ {
+ return new EquatableArray(values);
+ }
+}
+
+///
+/// An immutable, equatable array. This is equivalent to but with value equality support.
+///
+/// The type of values in the array.
+internal readonly struct EquatableArray : IEquatable>
+ where T : IEquatable
+{
+ public static EquatableArray Empty { get; } = new();
+
+ ///
+ /// The underlying array.
+ ///
+ private readonly ImmutableArray _array;
+
+ public int Length => _array.Length;
+
+ ///
+ /// Creates a new instance.
+ ///
+ public EquatableArray()
+ {
+ _array = [];
+ }
+
+ ///
+ /// Creates a new instance.
+ ///
+ /// The input to wrap.
+ public EquatableArray(T[] array)
+ {
+ _array = [.. array];
+ }
+
+ ///
+ /// Creates a new instance.
+ ///
+ /// The input to wrap.
+ public EquatableArray(IEnumerable values)
+ {
+ _array = [.. values];
+ }
+
+ ///
+ public bool Equals(EquatableArray array)
+ {
+ return AsSpan().SequenceEqual(array.AsSpan());
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ return obj is EquatableArray array && this.Equals(array);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ if (_array.IsDefaultOrEmpty)
+ {
+ return 0;
+ }
+
+ HashCode hashCode = default;
+
+ foreach (T item in _array)
+ {
+ hashCode.Add(item);
+ }
+
+ return hashCode.ToHashCode();
+ }
+
+ ///
+ /// Returns a wrapping the current items.
+ ///
+ /// A wrapping the current items.
+ public ReadOnlySpan AsSpan()
+ {
+ return _array.AsSpan();
+ }
+
+ ///
+ /// Returns an enumerator for the contents of the array.
+ ///
+ /// An enumerator.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ImmutableArray.Enumerator GetEnumerator()
+ {
+ return _array.GetEnumerator();
+ }
+
+ ///
+ /// Checks whether two values are the same.
+ ///
+ /// The first value.
+ /// The second value.
+ /// Whether and are equal.
+ public static bool operator ==(EquatableArray left, EquatableArray right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Checks whether two values are not the same.
+ ///
+ /// The first value.
+ /// The second value.
+ /// Whether and are not equal.
+ public static bool operator !=(EquatableArray left, EquatableArray right)
+ {
+ return !left.Equals(right);
+ }
+}
\ No newline at end of file
diff --git a/src/Pipelines.Generator/Primitives/HashCode.cs b/src/Pipelines.Generator/Primitives/HashCode.cs
new file mode 100644
index 0000000..33a5280
--- /dev/null
+++ b/src/Pipelines.Generator/Primitives/HashCode.cs
@@ -0,0 +1,383 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace System;
+
+///
+/// Polyfill for .NET 6 HashCode
+///
+internal struct HashCode
+{
+ private static readonly uint s_seed = GenerateGlobalSeed();
+
+ private const uint Prime1 = 2654435761U;
+ private const uint Prime2 = 2246822519U;
+ private const uint Prime3 = 3266489917U;
+ private const uint Prime4 = 668265263U;
+ private const uint Prime5 = 374761393U;
+
+ private uint _v1, _v2, _v3, _v4;
+ private uint _queue1, _queue2, _queue3;
+ private uint _length;
+
+ private static uint GenerateGlobalSeed()
+ {
+ var buffer = new byte[sizeof(uint)];
+#pragma warning disable RS1035 // Do not use APIs banned for analyzers
+ new Random().NextBytes(buffer);
+#pragma warning restore RS1035 // Do not use APIs banned for analyzers
+ return BitConverter.ToUInt32(buffer, 0);
+ }
+
+ public static int Combine(T1 value1)
+ {
+ // Provide a way of diffusing bits from something with a limited
+ // input hash space. For example, many enums only have a few
+ // possible hashes, only using the bottom few bits of the code. Some
+ // collections are built on the assumption that hashes are spread
+ // over a larger space, so diffusing the bits may help the
+ // collection work more efficiently.
+
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+
+ uint hash = MixEmptyState();
+ hash += 4;
+
+ hash = QueueRound(hash, hc1);
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ public static int Combine(T1 value1, T2 value2)
+ {
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+ uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
+
+ uint hash = MixEmptyState();
+ hash += 8;
+
+ hash = QueueRound(hash, hc1);
+ hash = QueueRound(hash, hc2);
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ public static int Combine(T1 value1, T2 value2, T3 value3)
+ {
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+ uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
+ uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
+
+ uint hash = MixEmptyState();
+ hash += 12;
+
+ hash = QueueRound(hash, hc1);
+ hash = QueueRound(hash, hc2);
+ hash = QueueRound(hash, hc3);
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4)
+ {
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+ uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
+ uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
+ uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
+
+ Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
+
+ v1 = Round(v1, hc1);
+ v2 = Round(v2, hc2);
+ v3 = Round(v3, hc3);
+ v4 = Round(v4, hc4);
+
+ uint hash = MixState(v1, v2, v3, v4);
+ hash += 16;
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5)
+ {
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+ uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
+ uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
+ uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
+ uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
+
+ Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
+
+ v1 = Round(v1, hc1);
+ v2 = Round(v2, hc2);
+ v3 = Round(v3, hc3);
+ v4 = Round(v4, hc4);
+
+ uint hash = MixState(v1, v2, v3, v4);
+ hash += 20;
+
+ hash = QueueRound(hash, hc5);
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6)
+ {
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+ uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
+ uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
+ uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
+ uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
+ uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
+
+ Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
+
+ v1 = Round(v1, hc1);
+ v2 = Round(v2, hc2);
+ v3 = Round(v3, hc3);
+ v4 = Round(v4, hc4);
+
+ uint hash = MixState(v1, v2, v3, v4);
+ hash += 24;
+
+ hash = QueueRound(hash, hc5);
+ hash = QueueRound(hash, hc6);
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7)
+ {
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+ uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
+ uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
+ uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
+ uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
+ uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
+ uint hc7 = (uint)(value7?.GetHashCode() ?? 0);
+
+ Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
+
+ v1 = Round(v1, hc1);
+ v2 = Round(v2, hc2);
+ v3 = Round(v3, hc3);
+ v4 = Round(v4, hc4);
+
+ uint hash = MixState(v1, v2, v3, v4);
+ hash += 28;
+
+ hash = QueueRound(hash, hc5);
+ hash = QueueRound(hash, hc6);
+ hash = QueueRound(hash, hc7);
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8)
+ {
+ uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
+ uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
+ uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
+ uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
+ uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
+ uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
+ uint hc7 = (uint)(value7?.GetHashCode() ?? 0);
+ uint hc8 = (uint)(value8?.GetHashCode() ?? 0);
+
+ Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
+
+ v1 = Round(v1, hc1);
+ v2 = Round(v2, hc2);
+ v3 = Round(v3, hc3);
+ v4 = Round(v4, hc4);
+
+ v1 = Round(v1, hc5);
+ v2 = Round(v2, hc6);
+ v3 = Round(v3, hc7);
+ v4 = Round(v4, hc8);
+
+ uint hash = MixState(v1, v2, v3, v4);
+ hash += 32;
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4)
+ {
+ v1 = s_seed + Prime1 + Prime2;
+ v2 = s_seed + Prime2;
+ v3 = s_seed;
+ v4 = s_seed - Prime1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint Round(uint hash, uint input)
+ {
+ return RotateLeft(hash + input * Prime2, 13) * Prime1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint QueueRound(uint hash, uint queuedValue)
+ {
+ return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint MixState(uint v1, uint v2, uint v3, uint v4)
+ {
+ return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18);
+ }
+
+ private static uint MixEmptyState()
+ {
+ return s_seed + Prime5;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint MixFinal(uint hash)
+ {
+ hash ^= hash >> 15;
+ hash *= Prime2;
+ hash ^= hash >> 13;
+ hash *= Prime3;
+ hash ^= hash >> 16;
+ return hash;
+ }
+
+ public void Add(T value)
+ {
+ Add(value?.GetHashCode() ?? 0);
+ }
+
+ public void Add(T value, IEqualityComparer? comparer)
+ {
+ Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode()));
+ }
+
+ private void Add(int value)
+ {
+ // The original xxHash works as follows:
+ // 0. Initialize immediately. We can't do this in a struct (no
+ // default ctor).
+ // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators.
+ // 2. Accumulate remaining blocks of length 4 (1 uint) into the
+ // hash.
+ // 3. Accumulate remaining blocks of length 1 into the hash.
+
+ // There is no need for #3 as this type only accepts ints. _queue1,
+ // _queue2 and _queue3 are basically a buffer so that when
+ // ToHashCode is called we can execute #2 correctly.
+
+ // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see
+ // #0) nd the last place that can be done if you look at the
+ // original code is just before the first block of 16 bytes is mixed
+ // in. The xxHash32 state is never used for streams containing fewer
+ // than 16 bytes.
+
+ // To see what's really going on here, have a look at the Combine
+ // methods.
+
+ uint val = (uint)value;
+
+ // Storing the value of _length locally shaves of quite a few bytes
+ // in the resulting machine code.
+ uint previousLength = _length++;
+ uint position = previousLength % 4;
+
+ // Switch can't be inlined.
+
+ if (position == 0)
+ _queue1 = val;
+ else if (position == 1)
+ _queue2 = val;
+ else if (position == 2)
+ _queue3 = val;
+ else // position == 3
+ {
+ if (previousLength == 3)
+ Initialize(out _v1, out _v2, out _v3, out _v4);
+
+ _v1 = Round(_v1, _queue1);
+ _v2 = Round(_v2, _queue2);
+ _v3 = Round(_v3, _queue3);
+ _v4 = Round(_v4, val);
+ }
+ }
+
+ public int ToHashCode()
+ {
+ // Storing the value of _length locally shaves of quite a few bytes
+ // in the resulting machine code.
+ uint length = _length;
+
+ // position refers to the *next* queue position in this method, so
+ // position == 1 means that _queue1 is populated; _queue2 would have
+ // been populated on the next call to Add.
+ uint position = length % 4;
+
+ // If the length is less than 4, _v1 to _v4 don't contain anything
+ // yet. xxHash32 treats this differently.
+
+ uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4);
+
+ // _length is incremented once per Add(Int32) and is therefore 4
+ // times too small (xxHash length is in bytes, not ints).
+
+ hash += length * 4;
+
+ // Mix what remains in the queue
+
+ // Switch can't be inlined right now, so use as few branches as
+ // possible by manually excluding impossible scenarios (position > 1
+ // is always false if position is not > 0).
+ if (position > 0)
+ {
+ hash = QueueRound(hash, _queue1);
+ if (position > 1)
+ {
+ hash = QueueRound(hash, _queue2);
+ if (position > 2)
+ hash = QueueRound(hash, _queue3);
+ }
+ }
+
+ hash = MixFinal(hash);
+ return (int)hash;
+ }
+
+#pragma warning disable 0809
+ // Obsolete member 'memberA' overrides non-obsolete member 'memberB'.
+ // Disallowing GetHashCode and Equals is by design
+
+ // * We decided to not override GetHashCode() to produce the hash code
+ // as this would be weird, both naming-wise as well as from a
+ // behavioral standpoint (GetHashCode() should return the object's
+ // hash code, not the one being computed).
+
+ // * Even though ToHashCode() can be called safely multiple times on
+ // this implementation, it is not part of the contract. If the
+ // implementation has to change in the future we don't want to worry
+ // about people who might have incorrectly used this type.
+
+ [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override int GetHashCode() => throw new NotSupportedException("Hash code not supported");
+
+ [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool Equals(object? obj) => throw new NotSupportedException("Equality not supported");
+#pragma warning restore 0809
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint RotateLeft(uint value, int offset)
+ => (value << offset) | (value >> (32 - offset));
+}
\ No newline at end of file
diff --git a/src/Pipelines/Attributes/IgnoreAttribute.cs b/src/Pipelines/Attributes/IgnoreAttribute.cs
new file mode 100644
index 0000000..1b706e9
--- /dev/null
+++ b/src/Pipelines/Attributes/IgnoreAttribute.cs
@@ -0,0 +1,15 @@
+namespace Pipelines.Attributes;
+
+///
+/// Marks that a handler should be ignored and not registered to di automatically.
+///
+[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+public sealed class IgnoreAttribute : Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public IgnoreAttribute()
+ {
+ }
+}
diff --git a/test/Pipelines.Generator.Test.Integration/IgnoreAttributeTests.cs b/test/Pipelines.Generator.Test.Integration/IgnoreAttributeTests.cs
new file mode 100644
index 0000000..8d6f944
--- /dev/null
+++ b/test/Pipelines.Generator.Test.Integration/IgnoreAttributeTests.cs
@@ -0,0 +1,51 @@
+using Pipelines.Attributes;
+using Pipelines.Requests;
+
+namespace Pipelines.Generator.Test.Integration;
+
+public sealed class IgnoreAttributeTests : TestBase
+{
+ [Test]
+ public async Task Should_Not_Register_Handler()
+ {
+ var services = SetupServices();
+
+ var pingHandler = services.GetService>();
+ await That(pingHandler).IsNull();
+
+ var pongHandler = services.GetService>();
+ await That(pongHandler).IsNull();
+ }
+
+ [Test]
+ public async Task Should_Not_Resolve_Pipeline()
+ {
+ var services = SetupServices();
+
+ var pingPipline = () => services.GetService>();
+ await That(pingPipline).Throws().WithMessageContaining("Unable to resolve service");
+
+ var pongPipline = () => services.GetService>();
+ await That(pongPipline).Throws().WithMessageContaining("Unable to resolve service"); ;
+ }
+
+ public sealed class Ping : IRequest;
+
+ public sealed record Pong(int Num) : IRequest;
+
+ [Ignore]
+ public sealed class PingHandler(RequestContext context) : IRequestHandler, IRequestHandler
+ {
+ public ValueTask Handle(Ping request, CancellationToken cancellationToken = default)
+ {
+ context.CallStack.Push(GetType().Name);
+ return new(new Pong(100));
+ }
+
+ public ValueTask Handle(Pong request, CancellationToken cancellationToken = default)
+ {
+ context.CallStack.Push(GetType().Name);
+ return new(new Ping());
+ }
+ }
+}
diff --git a/test/Pipelines.Generator.Test/HandlersRegistrationTests.cs b/test/Pipelines.Generator.Test/HandlersRegistrationTests.cs
index fbf0360..a726331 100644
--- a/test/Pipelines.Generator.Test/HandlersRegistrationTests.cs
+++ b/test/Pipelines.Generator.Test/HandlersRegistrationTests.cs
@@ -73,37 +73,61 @@ public Task NoServiceCollectionAvailable()
[Test]
public Task PrivateHandler()
{
- return Verify(configure);
+ return Verify(context =>
+ {
+ configure(context);
+ context.IgnoreCompilationErrors = true;
+ });
}
[Test]
public Task PrivateRequest()
{
- return Verify(configure);
+ return Verify(context =>
+ {
+ configure(context);
+ context.IgnoreCompilationErrors = true;
+ });
}
[Test]
public Task PrivateResponse()
{
- return Verify(configure);
+ return Verify(context =>
+ {
+ configure(context);
+ context.IgnoreCompilationErrors = true;
+ });
}
[Test]
public Task ProtectedHandler()
{
- return Verify(configure);
+ return Verify(context =>
+ {
+ configure(context);
+ context.IgnoreCompilationErrors = true;
+ });
}
[Test]
public Task ProtectedRequest()
{
- return Verify(configure);
+ return Verify(context =>
+ {
+ configure(context);
+ context.IgnoreCompilationErrors = true;
+ });
}
[Test]
public Task ProtectedResponse()
{
- return Verify(configure);
+ return Verify(context =>
+ {
+ configure(context);
+ context.IgnoreCompilationErrors = true;
+ });
}
[Test]
@@ -118,6 +142,16 @@ public Task RequestWithoutContract()
return Verify(configure);
}
+ [Test]
+ public Task ServiceCollectionMethod()
+ {
+ return Verify(context =>
+ {
+ configure(context);
+ context.IgnoreCompilationErrors = true;
+ });
+ }
+
[Test]
public Task SimpleHandler()
{
diff --git a/test/Pipelines.Generator.Test/IgnoreAttributeTests.cs b/test/Pipelines.Generator.Test/IgnoreAttributeTests.cs
new file mode 100644
index 0000000..a99c37f
--- /dev/null
+++ b/test/Pipelines.Generator.Test/IgnoreAttributeTests.cs
@@ -0,0 +1,12 @@
+using System.Threading.Tasks;
+
+namespace Pipelines.Generator.Test;
+
+public sealed class IgnoreAttributeTests : TestBase
+{
+ [Test]
+ public Task SimpleHandler()
+ {
+ return Verify();
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Program.cs
index 056d622..c0be0be 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Program.cs
@@ -11,8 +11,10 @@ public static async Task Main()
public sealed class Result;
public abstract record Command : IRequest>
+ where TSelf : Command
where TResult : notnull;
+
public abstract class CommandHandler :
IRequestHandler>
where TCommand : Command
@@ -21,6 +23,14 @@ public abstract class CommandHandler :
public abstract ValueTask> Handle(TCommand command, CancellationToken cancellationToken);
}
+public abstract class StreamCommandHandler :
+ IStreamRequestHandler>
+ where TCommand : Command
+ where TResult : notnull
+{
+ public abstract IAsyncEnumerable> Handle(TCommand command, CancellationToken cancellationToken);
+}
+
// Define class from convention
public sealed record Pong;
@@ -34,3 +44,11 @@ public override ValueTask> Handle(Ping Ping, CancellationToken canc
return new(r);
}
}
+
+public sealed class PingStreamCommandHandler : StreamCommandHandler
+{
+ public override async IAsyncEnumerable> Handle(Ping Ping, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index bd220fd..c86231d 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/AbstractHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,6 +15,8 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped>, global::Pipelines.Test.Integration.PingCommandHandler>();
+ services.AddScoped>, global::Pipelines.Test.Integration.PingStreamCommandHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Program.cs
index 1072717..1e187f7 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Program.cs
@@ -11,4 +11,12 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ public sealed class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index 7652b9f..5ab2864 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DeepNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,6 +15,8 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped, global::Some.Very.Very.Very.Very.Deep.Namespace.ThatIUseToTestTheSourceGenSoThatItCanHandleLotsOfDifferentInput.PingHandler>();
+ services.AddScoped, global::Some.Very.Very.Very.Very.Deep.Namespace.ThatIUseToTestTheSourceGenSoThatItCanHandleLotsOfDifferentInput.PingStreamHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Program.cs
index 9441035..0ce436d 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Program.cs
@@ -17,3 +17,19 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+public sealed class PingStreamHandler : IStreamRequestHandler
+{
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
+
+public sealed class DuplicateStreamPingHandler : IStreamRequestHandler
+{
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index 3c54953..29891fc 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/DuplicateHandlers/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -16,6 +16,9 @@ internal static partial class PipelinesRegistrations
{
services.AddScoped, global::PingHandler>();
services.AddScoped, global::DuplicatePingHandler>();
+ services.AddScoped, global::PingStreamHandler>();
+ services.AddScoped, global::DuplicateStreamPingHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/FileHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/FileHandler/Program.cs
index 9494ebc..d075529 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/FileHandler/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/FileHandler/Program.cs
@@ -11,4 +11,12 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ file class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Program.cs
index f11e089..e3b8f07 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Program.cs
@@ -4,9 +4,10 @@ namespace Pipelines.Test.Integration;
public sealed class Result;
public abstract record Command : IRequest>
+ where TSelf : Command
where TResult : notnull;
-public class CommandHandler :
+public abstract class CommandHandler :
IRequestHandler>
where TCommand : Command
where TResult : notnull
@@ -14,6 +15,14 @@ public class CommandHandler :
public abstract ValueTask> Handle(TCommand command, CancellationToken cancellationToken);
}
+public abstract class StreamCommandHandler :
+ IStreamRequestHandler>
+ where TCommand : Command
+ where TResult : notnull
+{
+ public abstract IAsyncEnumerable> Handle(TCommand command, CancellationToken cancellationToken);
+}
+
public sealed record Pong;
public sealed record Ping : Command;
@@ -26,3 +35,11 @@ public override ValueTask> Handle(Ping Ping, CancellationToken canc
return new(r);
}
}
+
+public sealed class PingStreamCommandHandler : StreamCommandHandler
+{
+ public override async IAsyncEnumerable> Handle(Ping Ping, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index bd220fd..c86231d 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/GenericHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,6 +15,8 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped>, global::Pipelines.Test.Integration.PingCommandHandler>();
+ services.AddScoped>, global::Pipelines.Test.Integration.PingStreamCommandHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Program.cs
index aa91074..525d2ae 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Program.cs
@@ -14,4 +14,12 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new(Pong);
}
}
+
+ public sealed class StreamPingHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index 06ea9e7..50149da 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/InsideClass/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,6 +15,8 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped, global::Pipelines.Test.Integration.InsideClass.PingHandler>();
+ services.AddScoped, global::Pipelines.Test.Integration.InsideClass.StreamPingHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Program.cs
index fd5fbe6..6d0709e 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Program.cs
@@ -7,7 +7,7 @@ public sealed record Ping2();
public sealed record Pong();
public class Ping1Handler :
- IRequestHandler
+ IRequestHandler,
IRequestHandler
{
public ValueTask Handle(Ping1 request, CancellationToken cancellationToken)
@@ -20,3 +20,18 @@ public ValueTask Handle(Ping2 request, CancellationToken cancellationToken
return new ValueTask(new Pong());
}
}
+
+public class StreamPing1Handler :
+ IStreamRequestHandler,
+ IStreamRequestHandler
+{
+ public async IAsyncEnumerable Handle(Ping1 request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+
+ public async IAsyncEnumerable Handle(Ping2 request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index e467c70..5c37f5b 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/MultipleInterfaceImpl/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -16,6 +16,9 @@ internal static partial class PipelinesRegistrations
{
services.AddScoped, global::SomeProgram.Ping1Handler>();
services.AddScoped, global::SomeProgram.Ping1Handler>();
+ services.AddScoped, global::SomeProgram.StreamPing1Handler>();
+ services.AddScoped, global::SomeProgram.StreamPing1Handler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateHandler/Program.cs
index 47b95f9..ad8256e 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateHandler/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateHandler/Program.cs
@@ -17,5 +17,13 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ private class StreamPingHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateRequest/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateRequest/Program.cs
index d0505de..d85fdd6 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateRequest/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateRequest/Program.cs
@@ -2,7 +2,7 @@ namespace Some.Nested.Types
{
public static class Program
{
- public static void Task Main()
+ public static void Main()
{
}
@@ -17,5 +17,13 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateResponse/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateResponse/Program.cs
index 142303f..7b4d16e 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateResponse/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/PrivateResponse/Program.cs
@@ -2,7 +2,7 @@ namespace Some.Nested.Types
{
public static class Program
{
- public static void Task Main()
+ public static void Main()
{
}
@@ -17,5 +17,13 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedHandler/Program.cs
index ce80062..02bc22e 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedHandler/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedHandler/Program.cs
@@ -2,7 +2,7 @@ namespace Some.Nested.Types
{
public static class Program
{
- public static void Task Main()
+ public static void Main()
{
}
@@ -17,5 +17,13 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ protected class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedRequest/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedRequest/Program.cs
index 82e0c50..e59bf7c 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedRequest/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedRequest/Program.cs
@@ -2,7 +2,7 @@ namespace Some.Nested.Types
{
public static class Program
{
- public static void Task Main()
+ public static void Main()
{
}
@@ -17,5 +17,13 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedResponse/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedResponse/Program.cs
index fc02a1f..2717a3e 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedResponse/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ProtectedResponse/Program.cs
@@ -2,7 +2,7 @@ namespace Some.Nested.Types
{
public static class Program
{
- public static void Task Main()
+ public static void Main()
{
}
@@ -17,5 +17,13 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Program.cs
index aa91074..82a89ca 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Program.cs
@@ -2,7 +2,7 @@
public class InsideClass
{
- public sealed class Ping : IRequest;
+ public sealed class Ping : IRequest, IStreamRequest;
public sealed class Pong;
@@ -14,4 +14,12 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new(Pong);
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index 06ea9e7..5925b52 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,6 +15,8 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped, global::Pipelines.Test.Integration.InsideClass.PingHandler>();
+ services.AddScoped, global::Pipelines.Test.Integration.InsideClass.PingStreamHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Program.cs
index 9ba4543..31b9999 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Program.cs
@@ -14,4 +14,12 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new(Pong);
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index 06ea9e7..5925b52 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/RequestWithoutContract/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,6 +15,8 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped, global::Pipelines.Test.Integration.InsideClass.PingHandler>();
+ services.AddScoped, global::Pipelines.Test.Integration.InsideClass.PingStreamHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ServiceCollectionMethod/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ServiceCollectionMethod/Program.cs
new file mode 100644
index 0000000..216a9c1
--- /dev/null
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ServiceCollectionMethod/Program.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Some;
+
+public static class Program
+{
+ public static async Task Main()
+ {
+ var services = new ServiceCollection();
+
+ services.AddHandlers();
+
+ var serviceProvider = services.BuildServiceProvider();
+
+ var pipeline = serviceProvider.GetRequiredService();
+
+ _ = await pipeline.Send(new Ping(Guid.NewGuid()));
+ }
+
+ public sealed record Ping(Guid Id) : IRequest, IStreamRequest;
+
+ public sealed class PingHandler : IRequestHandler
+ {
+ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
+ {
+ var bytes = request.Id.ToByteArray();
+ return new ValueTask(bytes);
+ }
+ }
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ServiceCollectionMethod/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ServiceCollectionMethod/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
new file mode 100644
index 0000000..7bf659c
--- /dev/null
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/ServiceCollectionMethod/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -0,0 +1,24 @@
+
+#nullable enable
+
+using global::System.CodeDom.Compiler;
+using global::Microsoft.Extensions.DependencyInjection;
+using global::Microsoft.Extensions.DependencyInjection.Extensions;
+using global::Pipelines;
+using global::Pipelines.Requests;
+using global::Pipelines.Streams;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+internal static partial class PipelinesRegistrations
+{
+ public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
+ {
+ services.AddScoped, global::Some.Program.PingHandler>();
+ services.AddScoped, global::Some.Program.PingStreamHandler>();
+
+ return services;
+ }
+}
+
+#nullable disable
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Program.cs
index 3f7ef93..c107732 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Program.cs
@@ -11,4 +11,12 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index 4308f8a..6f8e4d4 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/SimpleHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,6 +15,8 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped, global::Some.Nested.Types.PingHandler>();
+ services.AddScoped, global::Some.Nested.Types.PingStreamHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/StructHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/StructHandler/Program.cs
index bba405d..f440a39 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/StructHandler/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/StructHandler/Program.cs
@@ -1,4 +1,4 @@
-public sealed record Ping(Guid Id) : IRequest;
+public sealed record Ping(Guid Id) : IRequest, IStreamRequest;
public sealed record Pong(Guid Id);
@@ -9,3 +9,11 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
return new ValueTask(new Pong(request.Id));
}
}
+
+public readonly struct PingStreamHandler : IStreamRequestHandler
+{
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Program.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Program.cs
index 7c78937..82741e9 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Program.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Program.cs
@@ -1,22 +1,6 @@
namespace Some.Nested.Types.One
{
- public static class Program
- {
- public static async Task Main()
- {
- var services = new ServiceCollection();
-
- services.AddBlackwing();
-
- var serviceProvider = services.BuildServiceProvider();
-
- var mediator = serviceProvider.GetRequiredService();
-
- _ = await mediator.Send(new Ping(Guid.NewGuid()));
- }
- }
-
- public sealed record Ping(Guid Id) : IRequest;
+ public sealed record Ping(Guid Id) : IRequest, IStreamRequest;
public sealed class PingHandler : IRequestHandler
{
@@ -26,11 +10,19 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToke
return new ValueTask(bytes);
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
namespace Some.Nested.Types.Two
{
- public sealed record Ping(Guid Id) : IRequest;
+ public sealed record Ping(Guid Id) : IRequest, IStreamRequest;
public sealed class PingHandler : IRequestHandler
{
@@ -40,4 +32,12 @@ public ValueTask Handle(Ping request, CancellationToken cancellationToke
return new ValueTask(bytes);
}
}
+
+ public class PingStreamHandler : IStreamRequestHandler
+ {
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+ }
}
diff --git a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
index 951133e..46e39a1 100644
--- a/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
+++ b/test/Pipelines.Generator.Test/Resources/HandlersRegistrationTests/TwoHandlersDifferentNamespace/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -15,7 +15,10 @@ internal static partial class PipelinesRegistrations
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
services.AddScoped, global::Some.Nested.Types.One.PingHandler>();
+ services.AddScoped, global::Some.Nested.Types.One.PingStreamHandler>();
services.AddScoped, global::Some.Nested.Types.Two.PingHandler>();
+ services.AddScoped, global::Some.Nested.Types.Two.PingStreamHandler>();
+
return services;
}
}
diff --git a/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Program.cs b/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Program.cs
new file mode 100644
index 0000000..96935cb
--- /dev/null
+++ b/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Program.cs
@@ -0,0 +1,39 @@
+namespace Simple;
+
+public sealed record Ping(Guid Id);
+
+public sealed record Pong(Guid Id);
+
+public class PingHandler : IRequestHandler
+{
+ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
+ {
+ return new ValueTask(new Pong(request.Id));
+ }
+}
+
+[Ignore]
+public class PingHandlerIgnore : IRequestHandler
+{
+ public ValueTask Handle(Ping request, CancellationToken cancellationToken)
+ {
+ return new ValueTask(new Pong(request.Id));
+ }
+}
+
+public class PingStream : IStreamRequestHandler
+{
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
+
+[Ignore]
+public class PingStreamIgnore : IStreamRequestHandler
+{
+ public async IAsyncEnumerable Handle(Ping request, CancellationToken cancellationToken)
+ {
+ yield break;
+ }
+}
diff --git a/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs b/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
new file mode 100644
index 0000000..b132807
--- /dev/null
+++ b/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Snapshot#PipelinesRegistrations.Handlers.g.verified.cs
@@ -0,0 +1,24 @@
+
+#nullable enable
+
+using global::System.CodeDom.Compiler;
+using global::Microsoft.Extensions.DependencyInjection;
+using global::Microsoft.Extensions.DependencyInjection.Extensions;
+using global::Pipelines;
+using global::Pipelines.Requests;
+using global::Pipelines.Streams;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+internal static partial class PipelinesRegistrations
+{
+ public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
+ {
+ services.AddScoped, global::Simple.PingHandler>();
+ services.AddScoped, global::Simple.PingStream>();
+
+ return services;
+ }
+}
+
+#nullable disable
diff --git a/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Snapshot#PipelinesRegistrations.Pipelines.g.verified.cs b/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Snapshot#PipelinesRegistrations.Pipelines.g.verified.cs
new file mode 100644
index 0000000..b37bd0c
--- /dev/null
+++ b/test/Pipelines.Generator.Test/Resources/IgnoreAttributeTests/SimpleHandler/Snapshot#PipelinesRegistrations.Pipelines.g.verified.cs
@@ -0,0 +1,25 @@
+
+#nullable enable
+
+using global::System.CodeDom.Compiler;
+using global::Microsoft.Extensions.DependencyInjection;
+using global::Microsoft.Extensions.DependencyInjection.Extensions;
+using global::Pipelines;
+using global::Pipelines.Requests;
+using global::Pipelines.Streams;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+internal static partial class PipelinesRegistrations
+{
+ public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddPipelines(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
+ {
+ services.TryAddScoped(typeof(global::Pipelines.Requests.RequestPipeline<,>));
+ services.TryAddScoped(typeof(global::Pipelines.Streams.StreamRequestPipeline<,>));
+ services.TryAddScoped();
+
+ return services;
+ }
+}
+
+#nullable disable
diff --git a/test/Pipelines.Generator.Test/_setup.cs b/test/Pipelines.Generator.Test/_setup.cs
index 3ddfaed..92159e9 100644
--- a/test/Pipelines.Generator.Test/_setup.cs
+++ b/test/Pipelines.Generator.Test/_setup.cs
@@ -33,6 +33,7 @@ .. AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic).Select(x =>
[
"System",
"System.Collections.Generic",
+ "System.Linq",
"System.Runtime.CompilerServices",
"System.Threading",
"System.Threading.Tasks",
@@ -61,7 +62,12 @@ .. AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic).Select(x =>
///
/// Whether to ignore compilation errors or not.
///
- public bool IgnoreErrors { get; set; }
+ public bool IgnoreCompilationErrors { get; set; }
+
+ ///
+ /// Whether to ignore generator compilation errors or not.
+ ///
+ public bool IgnoreGeneratorErrors { get; set; }
///
/// References to include in the compilation.
@@ -132,6 +138,8 @@ protected async Task Verify(Action? configure = null, [CallerMember
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
);
+ AssertNoErrors(compilation.GetDiagnostics(), context.IgnoreCompilationErrors);
+
var generator = new PipelinesGenerator().AsSourceGenerator();
var driver = (GeneratorDriver)CSharpGeneratorDriver.Create([generator], driverOptions: new(default, true));
driver = driver.RunGenerators(compilation);
@@ -139,8 +147,8 @@ protected async Task Verify(Action? configure = null, [CallerMember
var runResult1 = driver.GetRunResult();
var runResult2 = driver.RunGenerators(compilation.Clone()).GetRunResult();
- AssertNoErrors(runResult1.Diagnostics, context.IgnoreErrors);
- AssertNoErrors(runResult2.Diagnostics, context.IgnoreErrors);
+ AssertNoErrors(runResult1.Diagnostics, context.IgnoreGeneratorErrors);
+ AssertNoErrors(runResult2.Diagnostics, context.IgnoreGeneratorErrors);
//AssertRunsEqual(runResult1, runResult2, trackingNames);
await Verifier