From 530cd4cee78de7cdec5bbfd6d3912c1b1dda8bb2 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Wed, 13 Apr 2022 16:02:04 +0100 Subject: [PATCH] Support IObservable input/output (#236) --- .../Configuration/ServerBinder.cs | 2 +- .../Internal/ContractOperation.cs | 223 +++++---- .../Internal/MetadataContext.cs | 16 +- src/protobuf-net.Grpc/Internal/Reshape.cs | 431 +++++++++++++++++- .../Internal/ServerInvokerLookup.cs | 173 ++++--- .../StreamTests.cs | 211 ++++++++- .../protobuf-net.Grpc.Test.Integration.csproj | 1 + ...uf-net.Grpc.Test.IntegrationUpLevel.csproj | 1 + .../ContractOperationTests.cs | 181 ++++---- tests/protobuf-net.Grpc.Test/IAllOptions.cs | 29 ++ 10 files changed, 1023 insertions(+), 245 deletions(-) diff --git a/src/protobuf-net.Grpc/Configuration/ServerBinder.cs b/src/protobuf-net.Grpc/Configuration/ServerBinder.cs index 3e9bc8f5..4ca81245 100644 --- a/src/protobuf-net.Grpc/Configuration/ServerBinder.cs +++ b/src/protobuf-net.Grpc/Configuration/ServerBinder.cs @@ -52,7 +52,7 @@ public int Bind(object state, Type serviceType, BinderConfiguration? binderConfi var bindCtx = new ServiceBindContext(serviceContract, serviceType, state, binderConfiguration.Binder); foreach (var op in ContractOperation.FindOperations(binderConfiguration, serviceContract, this)) { - if (ServerInvokerLookup.TryGetValue(op.MethodType, op.Context, op.Result, op.Void, out var invoker) + if (ServerInvokerLookup.TryGetValue(op.MethodType, op.Context, op.Arg, op.Result, op.Void, out var invoker) && AddMethod(op.From, op.To, op.Name, op.Method, op.MethodType, invoker, bindCtx, serviceContractSimplifiedExceptions || op.Method.IsDefined(typeof(SimpleRpcExceptionsAttribute)) )) diff --git a/src/protobuf-net.Grpc/Internal/ContractOperation.cs b/src/protobuf-net.Grpc/Internal/ContractOperation.cs index d2a337eb..755ec521 100644 --- a/src/protobuf-net.Grpc/Internal/ContractOperation.cs +++ b/src/protobuf-net.Grpc/Internal/ContractOperation.cs @@ -17,6 +17,7 @@ internal readonly struct ContractOperation public MethodInfo Method { get; } public MethodType MethodType { get; } public ContextKind Context { get; } + public ResultKind Arg { get; } public ResultKind Result { get; } public VoidKind Void { get; } public bool VoidRequest => (Void & VoidKind.Request) != 0; @@ -25,7 +26,7 @@ internal readonly struct ContractOperation public override string ToString() => $"{Name}: {From.Name}=>{To.Name}, {MethodType}, {Result}, {Context}, {Void}"; public ContractOperation(string name, Type from, Type to, MethodInfo method, - MethodType methodType, ContextKind contextKind, ResultKind resultKind, VoidKind @void) + MethodType methodType, ContextKind contextKind, ResultKind arg, ResultKind resultKind, VoidKind @void) { Name = name; From = from; @@ -33,6 +34,7 @@ public ContractOperation(string name, Type from, Type to, MethodInfo method, Method = method; MethodType = methodType; Context = contextKind; + Arg = arg; Result = resultKind; Void = @void; } @@ -48,6 +50,7 @@ internal enum TypeCategory IAsyncEnumerable, IAsyncStreamReader, IServerStreamWriter, + IObservable, CallOptions, ServerCallContext, CallContext, @@ -61,94 +64,123 @@ internal enum TypeCategory } const int RET = -1, VOID = -2; - private static readonly Dictionary<(TypeCategory Arg0, TypeCategory Arg1, TypeCategory Arg2, TypeCategory Ret), (ContextKind Context, MethodType Method, ResultKind Result, VoidKind Void, int From, int To)> - s_signaturePatterns = new Dictionary<(TypeCategory, TypeCategory, TypeCategory, TypeCategory), (ContextKind, MethodType, ResultKind, VoidKind, int, int)> + private static readonly Dictionary<(TypeCategory Arg0, TypeCategory Arg1, TypeCategory Arg2, TypeCategory Ret), (ContextKind Context, MethodType Method, ResultKind Arg, ResultKind Result, VoidKind Void, int From, int To)> + s_signaturePatterns = new Dictionary<(TypeCategory, TypeCategory, TypeCategory, TypeCategory), (ContextKind, MethodType, ResultKind, ResultKind, VoidKind, int, int)> { // google server APIs - { (TypeCategory.IAsyncStreamReader, TypeCategory.ServerCallContext, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.ServerCallContext, MethodType.ClientStreaming, ResultKind.Task, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncStreamReader, TypeCategory.IServerStreamWriter, TypeCategory.ServerCallContext, TypeCategory.UntypedTask), (ContextKind.ServerCallContext, MethodType.DuplexStreaming, ResultKind.Task, VoidKind.None, 0, 1) }, - { (TypeCategory.Data, TypeCategory.IServerStreamWriter, TypeCategory.ServerCallContext, TypeCategory.UntypedTask), (ContextKind.ServerCallContext, MethodType.ServerStreaming, ResultKind.Task, VoidKind.None, 0, 1) }, - { (TypeCategory.Data, TypeCategory.ServerCallContext, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.ServerCallContext, MethodType.Unary, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncStreamReader, TypeCategory.ServerCallContext, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.ServerCallContext, MethodType.ClientStreaming, ResultKind.Grpc, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncStreamReader, TypeCategory.IServerStreamWriter, TypeCategory.ServerCallContext, TypeCategory.UntypedTask), (ContextKind.ServerCallContext, MethodType.DuplexStreaming, ResultKind.Grpc, ResultKind.Task, VoidKind.None, 0, 1) }, + { (TypeCategory.Data, TypeCategory.IServerStreamWriter, TypeCategory.ServerCallContext, TypeCategory.UntypedTask), (ContextKind.ServerCallContext, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.Task, VoidKind.None, 0, 1) }, + { (TypeCategory.Data, TypeCategory.ServerCallContext, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.ServerCallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.None, 0, RET) }, // google client APIs - { (TypeCategory.Data, TypeCategory.CallOptions, TypeCategory.None, TypeCategory.AsyncUnaryCall), (ContextKind.CallOptions, MethodType.Unary, ResultKind.Grpc, VoidKind.None, 0, RET) }, - { (TypeCategory.CallOptions, TypeCategory.None, TypeCategory.None, TypeCategory.AsyncClientStreamingCall), (ContextKind.CallOptions, MethodType.ClientStreaming, ResultKind.Grpc, VoidKind.None, RET, RET) }, - { (TypeCategory.CallOptions, TypeCategory.None, TypeCategory.None, TypeCategory.AsyncDuplexStreamingCall), (ContextKind.CallOptions, MethodType.DuplexStreaming, ResultKind.Grpc, VoidKind.None, RET, RET) }, - { (TypeCategory.Data, TypeCategory.CallOptions, TypeCategory.None, TypeCategory.AsyncServerStreamingCall), (ContextKind.CallOptions, MethodType.ServerStreaming, ResultKind.Grpc, VoidKind.None, 0, RET) }, - { (TypeCategory.Data, TypeCategory.CallOptions, TypeCategory.None, TypeCategory.Data), (ContextKind.CallOptions, MethodType.Unary, ResultKind.Sync, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CallOptions, TypeCategory.None, TypeCategory.AsyncUnaryCall), (ContextKind.CallOptions, MethodType.Unary, ResultKind.Sync, ResultKind.Grpc, VoidKind.None, 0, RET) }, + { (TypeCategory.CallOptions, TypeCategory.None, TypeCategory.None, TypeCategory.AsyncClientStreamingCall), (ContextKind.CallOptions, MethodType.ClientStreaming, ResultKind.Grpc, ResultKind.Grpc, VoidKind.None, RET, RET) }, + { (TypeCategory.CallOptions, TypeCategory.None, TypeCategory.None, TypeCategory.AsyncDuplexStreamingCall), (ContextKind.CallOptions, MethodType.DuplexStreaming, ResultKind.Grpc, ResultKind.Grpc, VoidKind.None, RET, RET) }, + { (TypeCategory.Data, TypeCategory.CallOptions, TypeCategory.None, TypeCategory.AsyncServerStreamingCall), (ContextKind.CallOptions, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.Grpc, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CallOptions, TypeCategory.None, TypeCategory.Data), (ContextKind.CallOptions, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.None, 0, RET) }, // unary parameterless, with or without a return value - { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.Void), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, VoidKind.Both, VOID, VOID)}, - { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.Data), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, VoidKind.Request, VOID, RET)}, - { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Task, VoidKind.Both, VOID, VOID) }, - { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.Both, VOID, VOID) }, - { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Task, VoidKind.Request, VOID, RET) }, - { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.Request, VOID, RET) }, - - { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.Void), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, VoidKind.Both,VOID, VOID)}, - { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.Data), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, VoidKind.Request, VOID, RET)}, - { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Task, VoidKind.Both, VOID, VOID) }, - { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.Both,VOID, VOID) }, - { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Task, VoidKind.Request, VOID, RET) }, - { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.Request, VOID, RET) }, - - { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.Void), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, VoidKind.Both,VOID, VOID)}, - { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.Data), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, VoidKind.Request, VOID, RET)}, - { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Task, VoidKind.Both, VOID, VOID) }, - { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.ValueTask, VoidKind.Both,VOID, VOID) }, - { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Task, VoidKind.Request, VOID, RET) }, - { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.ValueTask, VoidKind.Request, VOID, RET) }, + { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.Void), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Both, VOID, VOID)}, + { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.Data), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Request, VOID, RET)}, + { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Both, VOID, VOID) }, + { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Both, VOID, VOID) }, + { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Request, VOID, RET) }, + { (TypeCategory.None,TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Request, VOID, RET) }, + + { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.Void), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Both,VOID, VOID)}, + { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.Data), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Request, VOID, RET)}, + { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Both, VOID, VOID) }, + { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Both,VOID, VOID) }, + { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Request, VOID, RET) }, + { (TypeCategory.CallContext,TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Request, VOID, RET) }, + + { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.Void), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Both,VOID, VOID)}, + { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.Data), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Request, VOID, RET)}, + { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Both, VOID, VOID) }, + { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Both,VOID, VOID) }, + { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Request, VOID, RET) }, + { (TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Request, VOID, RET) }, // unary with parameter, with or without a return value - { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.Void), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, VoidKind.Response, 0, VOID)}, - { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.Data), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, VoidKind.None, 0, RET)}, - { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Task, VoidKind.Response, 0, VOID) }, - { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, - { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.TypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Task, VoidKind.None, 0, RET) }, - { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.None, 0, RET) }, - - { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.Void), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, VoidKind.Response,0, VOID)}, - { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.Data), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, VoidKind.None, 0, RET)}, - { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Task, VoidKind.Response, 0, VOID) }, - { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.Response,0, VOID) }, - { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Task, VoidKind.None, 0, RET) }, - { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.ValueTask, VoidKind.None, 0, RET) }, - - { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.Void), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, VoidKind.Response,0, VOID)}, - { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.Data), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, VoidKind.None, 0, RET)}, - { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Task, VoidKind.Response, 0, VOID) }, - { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.ValueTask, VoidKind.Response,0, VOID) }, - { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Task, VoidKind.None, 0, RET) }, - { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.Void), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Response, 0, VOID)}, + { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.Data), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.None, 0, RET)}, + { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Response, 0, VOID) }, + { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, + { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.TypedTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.None,TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.NoContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + + { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.Void), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Response,0, VOID)}, + { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.Data), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.None, 0, RET)}, + { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Response, 0, VOID) }, + { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Response,0, VOID) }, + { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CallContext,TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CallContext, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + + { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.Void), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Response,0, VOID)}, + { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.Data), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.None, 0, RET)}, + { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Response, 0, VOID) }, + { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Response,0, VOID) }, + { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CancellationToken,TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CancellationToken, MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.None, 0, RET) }, // client streaming - { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.Task, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.Task, VoidKind.Response, 0, VOID) }, - - { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.Task, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.Task, VoidKind.Response, 0, VOID) }, - - { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.Task, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.Task, VoidKind.Response, 0, VOID) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.Response, 0, VOID) }, + + { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.Response, 0, VOID) }, + + { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.Response, 0, VOID) }, + + // (and for observable) + { (TypeCategory.IObservable, TypeCategory.None, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.None, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, + { (TypeCategory.IObservable, TypeCategory.None, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.None, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.NoContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.Response, 0, VOID) }, + + { (TypeCategory.IObservable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, + { (TypeCategory.IObservable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CallContext, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.Response, 0, VOID) }, + + { (TypeCategory.IObservable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.TypedValueTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.UntypedValueTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.Response, 0, VOID) }, + { (TypeCategory.IObservable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.TypedTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.UntypedTask), (ContextKind.CancellationToken, MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.Response, 0, VOID) }, // server streaming - { (TypeCategory.None, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.NoContext, MethodType.ServerStreaming, ResultKind.AsyncEnumerable, VoidKind.Request, VOID, RET) }, - { (TypeCategory.CallContext, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CallContext, MethodType.ServerStreaming, ResultKind.AsyncEnumerable, VoidKind.Request, VOID, RET) }, - { (TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CancellationToken, MethodType.ServerStreaming, ResultKind.AsyncEnumerable, VoidKind.Request, VOID, RET) }, - { (TypeCategory.Data, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.NoContext, MethodType.ServerStreaming, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, - { (TypeCategory.Data, TypeCategory.CallContext, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CallContext, MethodType.ServerStreaming, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, - { (TypeCategory.Data, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CancellationToken, MethodType.ServerStreaming, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + { (TypeCategory.None, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.NoContext, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.Request, VOID, RET) }, + { (TypeCategory.CallContext, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CallContext, MethodType.ServerStreaming, ResultKind.Sync,ResultKind.AsyncEnumerable, VoidKind.Request, VOID, RET) }, + { (TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CancellationToken, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.Request, VOID, RET) }, + { (TypeCategory.Data, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.NoContext, MethodType.ServerStreaming, ResultKind.Sync,ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CallContext, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CallContext, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CancellationToken, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + + // (and for observable) + { (TypeCategory.None, TypeCategory.None, TypeCategory.None, TypeCategory.IObservable), (ContextKind.NoContext, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.Observable, VoidKind.Request, VOID, RET) }, + { (TypeCategory.CallContext, TypeCategory.None, TypeCategory.None, TypeCategory.IObservable), (ContextKind.CallContext, MethodType.ServerStreaming, ResultKind.Sync,ResultKind.Observable, VoidKind.Request, VOID, RET) }, + { (TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.None, TypeCategory.IObservable), (ContextKind.CancellationToken, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.Observable, VoidKind.Request, VOID, RET) }, + { (TypeCategory.Data, TypeCategory.None, TypeCategory.None, TypeCategory.IObservable), (ContextKind.NoContext, MethodType.ServerStreaming, ResultKind.Sync,ResultKind.Observable, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CallContext, TypeCategory.None, TypeCategory.IObservable), (ContextKind.CallContext, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.Observable, VoidKind.None, 0, RET) }, + { (TypeCategory.Data, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.IObservable), (ContextKind.CancellationToken, MethodType.ServerStreaming, ResultKind.Sync, ResultKind.Observable, VoidKind.None, 0, RET) }, // duplex - { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.NoContext, MethodType.DuplexStreaming, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CallContext, MethodType.DuplexStreaming, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, - { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CancellationToken, MethodType.DuplexStreaming, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.None, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.NoContext, MethodType.DuplexStreaming, ResultKind.AsyncEnumerable,ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CallContext, MethodType.DuplexStreaming, ResultKind.AsyncEnumerable, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + { (TypeCategory.IAsyncEnumerable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.IAsyncEnumerable), (ContextKind.CancellationToken, MethodType.DuplexStreaming, ResultKind.AsyncEnumerable, ResultKind.AsyncEnumerable, VoidKind.None, 0, RET) }, + + // (and for observable) + { (TypeCategory.IObservable, TypeCategory.None, TypeCategory.None, TypeCategory.IObservable), (ContextKind.NoContext, MethodType.DuplexStreaming, ResultKind.Observable,ResultKind.Observable, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.CallContext, TypeCategory.None, TypeCategory.IObservable), (ContextKind.CallContext, MethodType.DuplexStreaming, ResultKind.Observable, ResultKind.Observable, VoidKind.None, 0, RET) }, + { (TypeCategory.IObservable, TypeCategory.CancellationToken, TypeCategory.None, TypeCategory.IObservable), (ContextKind.CancellationToken, MethodType.DuplexStreaming, ResultKind.Observable, ResultKind.Observable, VoidKind.None, 0, RET) }, }; internal static int SignatureCount => s_signaturePatterns.Count; @@ -173,6 +205,7 @@ static TypeCategory GetCategory(MarshallerCache marshallerCache, Type type, IBin if (genType == typeof(IAsyncEnumerable<>)) return TypeCategory.IAsyncEnumerable; if (genType == typeof(IAsyncStreamReader<>)) return TypeCategory.IAsyncStreamReader; if (genType == typeof(IServerStreamWriter<>)) return TypeCategory.IServerStreamWriter; + if (genType == typeof(IObservable<>)) return TypeCategory.IObservable; if (genType == typeof(AsyncUnaryCall<>)) return TypeCategory.AsyncUnaryCall; if (genType == typeof(AsyncClientStreamingCall<,>)) return TypeCategory.AsyncClientStreamingCall; if (genType == typeof(AsyncDuplexStreamingCall<,>)) return TypeCategory.AsyncDuplexStreamingCall; @@ -205,7 +238,7 @@ internal static bool TryIdentifySignature(MethodInfo method, BinderConfiguration if (method.IsGenericMethodDefinition) return false; // can't work with methods if ((method.Attributes & (MethodAttributes.SpecialName)) != 0) return false; // some kind of accessor etc - + if (!binderConfig.Binder.IsOperationContract(method, out var opName)) return false; var args = method.GetParameters(); @@ -248,6 +281,7 @@ static Type GetDataType((Type type, TypeCategory category) key, bool req) case TypeCategory.IServerStreamWriter: case TypeCategory.AsyncUnaryCall: case TypeCategory.AsyncServerStreamingCall: + case TypeCategory.IObservable: return type.GetGenericArguments()[0]; case TypeCategory.AsyncClientStreamingCall: case TypeCategory.AsyncDuplexStreamingCall: @@ -260,7 +294,7 @@ static Type GetDataType((Type type, TypeCategory category) key, bool req) var from = GetDataType(GetTypeByIndex(config.From), true); var to = GetDataType(GetTypeByIndex(config.To), false); - operation = new ContractOperation(opName!, from, to, method, config.Method, config.Context, config.Result, config.Void); + operation = new ContractOperation(opName!, from, to, method, config.Method, config.Context, config.Arg, config.Result, config.Void); return true; } public static List FindOperations(BinderConfiguration binderConfig, Type contractType, IBindContext? bindContext) @@ -309,20 +343,26 @@ where parameters.Length > 1 && parameters[0].ParameterType == typeof(CallContext).MakeByRefType() select method).ToDictionary(x => x.Name); - static readonly Dictionary<(MethodType, ResultKind, VoidKind), string> _clientResponseMap = new Dictionary<(MethodType, ResultKind, VoidKind), string> + static readonly Dictionary<(MethodType, ResultKind, ResultKind, VoidKind), string> _clientResponseMap = new Dictionary<(MethodType, ResultKind, ResultKind, VoidKind), string> { - {(MethodType.DuplexStreaming, ResultKind.AsyncEnumerable, VoidKind.None), nameof(Reshape.DuplexAsync) }, - {(MethodType.ServerStreaming, ResultKind.AsyncEnumerable, VoidKind.None), nameof(Reshape.ServerStreamingAsync) }, - {(MethodType.ClientStreaming, ResultKind.Task, VoidKind.None), nameof(Reshape.ClientStreamingTaskAsync) }, - {(MethodType.ClientStreaming, ResultKind.Task, VoidKind.Response), nameof(Reshape.ClientStreamingTaskAsync) }, // Task works as Task - {(MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.None), nameof(Reshape.ClientStreamingValueTaskAsync) }, - {(MethodType.ClientStreaming, ResultKind.ValueTask, VoidKind.Response), nameof(Reshape.ClientStreamingValueTaskAsyncVoid) }, - {(MethodType.Unary, ResultKind.Task, VoidKind.None), nameof(Reshape.UnaryTaskAsync) }, - {(MethodType.Unary, ResultKind.Task, VoidKind.Response), nameof(Reshape.UnaryTaskAsync) }, // Task works as Task - {(MethodType.Unary, ResultKind.ValueTask, VoidKind.None), nameof(Reshape.UnaryValueTaskAsync) }, - {(MethodType.Unary, ResultKind.ValueTask, VoidKind.Response), nameof(Reshape.UnaryValueTaskAsyncVoid) }, - {(MethodType.Unary, ResultKind.Sync, VoidKind.None), nameof(Reshape.UnarySync) }, - {(MethodType.Unary, ResultKind.Sync, VoidKind.Response), nameof(Reshape.UnarySyncVoid) }, + {(MethodType.DuplexStreaming, ResultKind.AsyncEnumerable, ResultKind.AsyncEnumerable, VoidKind.None), nameof(Reshape.DuplexAsync) }, + {(MethodType.DuplexStreaming, ResultKind.Observable, ResultKind.Observable, VoidKind.None), nameof(Reshape.DuplexObservable) }, + {(MethodType.ServerStreaming, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.None), nameof(Reshape.ServerStreamingAsync) }, + {(MethodType.ServerStreaming, ResultKind.Sync, ResultKind.Observable, VoidKind.None), nameof(Reshape.ServerStreamingObservable) }, + {(MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.None), nameof(Reshape.ClientStreamingTaskAsync) }, + {(MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.Response), nameof(Reshape.ClientStreamingTaskAsync) }, // Task works as Task + {(MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.None), nameof(Reshape.ClientStreamingValueTaskAsync) }, + {(MethodType.ClientStreaming, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.Response), nameof(Reshape.ClientStreamingValueTaskAsyncVoid) }, + {(MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.None), nameof(Reshape.ClientStreamingObservableTaskAsync) }, + {(MethodType.ClientStreaming, ResultKind.Observable, ResultKind.Task, VoidKind.Response), nameof(Reshape.ClientStreamingObservableTaskAsync) }, // Task works as Task + {(MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.None), nameof(Reshape.ClientStreamingObservableValueTaskAsync) }, + {(MethodType.ClientStreaming, ResultKind.Observable, ResultKind.ValueTask, VoidKind.Response), nameof(Reshape.ClientStreamingObservableValueTaskAsyncVoid) }, + {(MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.None), nameof(Reshape.UnaryTaskAsync) }, + {(MethodType.Unary, ResultKind.Sync, ResultKind.Task, VoidKind.Response), nameof(Reshape.UnaryTaskAsync) }, // Task works as Task + {(MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.None), nameof(Reshape.UnaryValueTaskAsync) }, + {(MethodType.Unary, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Response), nameof(Reshape.UnaryValueTaskAsyncVoid) }, + {(MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.None), nameof(Reshape.UnarySync) }, + {(MethodType.Unary, ResultKind.Sync, ResultKind.Sync, VoidKind.Response), nameof(Reshape.UnarySyncVoid) }, }; #pragma warning restore CS0618 @@ -334,7 +374,7 @@ where parameters.Length > 1 case ContextKind.CallContext: case ContextKind.NoContext: case ContextKind.CancellationToken: - return _clientResponseMap.TryGetValue((MethodType, Result, Void & VoidKind.Response), out var helper) ? helper : null; + return _clientResponseMap.TryGetValue((MethodType, Arg, Result, Void & VoidKind.Response), out var helper) ? helper : null; default: return null; }; @@ -382,6 +422,7 @@ internal enum ResultKind ValueTask, AsyncEnumerable, Grpc, + Observable, } [Flags] diff --git a/src/protobuf-net.Grpc/Internal/MetadataContext.cs b/src/protobuf-net.Grpc/Internal/MetadataContext.cs index 7857835f..9fadda8f 100644 --- a/src/protobuf-net.Grpc/Internal/MetadataContext.cs +++ b/src/protobuf-net.Grpc/Internal/MetadataContext.cs @@ -73,9 +73,10 @@ internal void SetTrailers(RpcException fault) } } - internal void SetTrailers(T call, Func getStatus, Func getMetadata) + internal void SetTrailers(T? call, Func getStatus, Func getMetadata) where T : class { + if (call is null) return; try { _trailers = getMetadata(call) ?? Metadata.Empty; @@ -88,10 +89,15 @@ internal void SetTrailers(T call, Func getStatus, Func headers) + internal ValueTask SetHeadersAsync(Task? headers) { + if (headers is null) return default; var tcs = Interlocked.CompareExchange(ref _headersTaskOrSource, headers, null) as TaskCompletionSource; - if (headers.RanToCompletion()) + if (tcs is null) + { + return new ValueTask(headers); + } + else if (headers.RanToCompletion()) { // headers are sync; update TCS if one tcs?.TrySetResult(headers.Result); @@ -103,11 +109,11 @@ internal ValueTask SetHeadersAsync(Task headers) return Awaited(this, tcs, headers); } - static async ValueTask Awaited(MetadataContext context, TaskCompletionSource? tcs, Task headers) + static async ValueTask Awaited(MetadataContext context, TaskCompletionSource tcs, Task headers) { try { - tcs?.TrySetResult(await headers.ConfigureAwait(false)); + tcs.TrySetResult(await headers.ConfigureAwait(false)); } catch (RpcException fault) { diff --git a/src/protobuf-net.Grpc/Internal/Reshape.cs b/src/protobuf-net.Grpc/Internal/Reshape.cs index 75a8b50d..4bed307a 100644 --- a/src/protobuf-net.Grpc/Internal/Reshape.cs +++ b/src/protobuf-net.Grpc/Internal/Reshape.cs @@ -3,9 +3,12 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using System.Threading.Tasks.Sources; + namespace ProtoBuf.Grpc.Internal { /// @@ -125,7 +128,74 @@ public static async IAsyncEnumerable AsAsyncEnumerable(this IAsyncStreamRe } /// - /// Consumes an asynchronous enumerable sequence and writes it to a server stream-writer + /// Interprets a stream-reader as an observable sequence + /// + public static IObservable AsObservable(this IAsyncStreamReader reader) + => new ReaderObservable(reader); + + private class ReaderObservable : IObservable, IDisposable + { + private readonly IAsyncStreamReader _reader; + private IObserver? _observer; + + public ReaderObservable(IAsyncStreamReader reader) + { + _reader = reader; + } + + IDisposable IObservable.Subscribe(IObserver observer) + { + + if (observer is null) ThrowNull(); + if (Interlocked.CompareExchange(ref _observer, observer, null) is not null) ThrowObserved(); + Task.Run(PushToObserver); + return this; + + static void ThrowNull() => throw new ArgumentNullException(nameof(observer)); + static void ThrowObserved() => throw new InvalidOperationException("The sequence is already being observed"); + } + public virtual void Dispose() => Volatile.Write(ref _observer, null); + private async Task PushToObserver() + { + // we don't *expect* eager dispose, and using a custom CT *on top of* the gRPC CT + // makes for perf complications; instead, we'll optimize for "consume everything", + // and in the rare occasion when the consumer stops early: we'll just handle it + try + { + Debug.WriteLine("starting observer publish", ToString()); + await OnBeforeAsync().ConfigureAwait(false); + while (await _reader.MoveNext(CancellationToken.None).ConfigureAwait(false)) + { + Debug.WriteLine($"forwarding: {_reader.Current}", ToString()); + Volatile.Read(ref _observer)?.OnNext(_reader.Current); + } + Debug.WriteLine($"completing observer publish", ToString()); + await OnAfterAsync().ConfigureAwait(false); + Volatile.Read(ref _observer)?.OnCompleted(); + } + catch (RpcException fault) + { + Debug.WriteLine($"observer fault: {fault.Message}", ToString()); + OnFault(fault); + Volatile.Read(ref _observer)?.OnError(fault); + } + catch (Exception ex) + { + Debug.WriteLine($"observer fault: {ex.Message}", ToString()); + Volatile.Read(ref _observer)?.OnError(ex); + } + finally + { + Dispose(); + } + } + protected virtual ValueTask OnBeforeAsync() => default; + protected virtual void OnFault(RpcException fault) { } + protected virtual ValueTask OnAfterAsync() => default; + } + + /// + /// Consumes an observable sequence and writes it to a server stream-writer /// [Obsolete(WarningMessage, false)] [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] @@ -137,6 +207,196 @@ public static async Task WriteTo(this IAsyncEnumerable reader, IServerStre } } + /// + /// Consumes an asynchronous enumerable sequence and writes it to a server stream-writer + /// + [Obsolete(WarningMessage, false)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public static Task WriteObservableTo(this IObservable reader, IAsyncStreamWriter writer) + => new WriterObserver().Subscribe(reader, writer); + + private sealed class WriterObserver : IObserver, IValueTaskSource +#if NETCOREAPP3_1_OR_GREATER + , IThreadPoolWorkItem +#endif + { +#if NETCOREAPP3_1_OR_GREATER + void IThreadPoolWorkItem.Execute() => Activate(); +#else + private static readonly WaitCallback s_Activate = static state => Unsafe.As>(state)!.Activate(); +#endif + public WriterObserver() + { + Debug.WriteLine("Creating...", ToString()); + } + private readonly Queue _backlog = new Queue(); + private volatile Exception? _fault; + private ManualResetValueTaskSourceCore _pendingWork; + [Flags] + private enum StateFlags + { + None = 0, + IsCompleted = 1 << 0, + NeedsActivation = 1 << 1, + } + private StateFlags _flags; + + public async Task Subscribe(IObservable reader, IAsyncStreamWriter writer) + { + Debug.WriteLine($"Subscribing...", ToString()); + await Task.Yield(); + var sub = reader?.Subscribe(this); + string debugStep = nameof(Subscribe); + try + { + if (reader is null) return; // nothing to write + debugStep = nameof(WaitForWorkAsync); + while (await WaitForWorkAsync().ConfigureAwait(false)) + { + // try to read synchronously as much as possible + while (true) + { + T next; + + debugStep = nameof(_backlog.Dequeue); + lock (_backlog) + { +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + if (!_backlog.TryDequeue(out next!)) break; +#else + if (_backlog.Count == 0) break; + next = _backlog.Dequeue(); +#endif + } + Debug.WriteLine($"Writing: {next}", ToString()); + debugStep = nameof(writer.WriteAsync); + await writer.WriteAsync(next).ConfigureAwait(false); + } + } + Debug.WriteLine($"Subscribe exiting with success", ToString()); + } + catch (Exception ex) + { + Debug.WriteLine($"Subscribe exiting with failure during {debugStep}: {ex.Message}", ToString()); + throw; + } + finally + { + lock (_backlog) + { // we won't be writing any more; formalize that + _backlog.Clear(); + _flags |= StateFlags.IsCompleted; + } + + if (writer is IClientStreamWriter client) + { + try // tell the writer that we're done, if possible + { + await client.CompleteAsync().ConfigureAwait(false); + } + catch { } + } + + try + { + sub?.Dispose(); + } + catch { } + } + } + private ValueTask WaitForWorkAsync() + { + lock (_backlog) + { + if (_backlog.Count != 0) return new ValueTask(true); + if ((_flags & StateFlags.IsCompleted) != 0) return new ValueTask(false); + _flags |= StateFlags.NeedsActivation; + Debug.WriteLine("Subscribe awaiting reactivation", ToString()); + return new ValueTask(this, _pendingWork.Version); + } + } + + private void Activate() + { + var fault = _fault; + if (fault is null) + { + _pendingWork.SetResult(true); // note this value is a dummy; the real value comes from GetResult + } + else + { + _pendingWork.SetException(fault); + } + } + private void ActivateIfNeededLocked() + { + if ((_flags & StateFlags.NeedsActivation) != 0) + { + _flags &= ~StateFlags.NeedsActivation; + Debug.WriteLine("Activating", ToString()); +#if NETCOREAPP3_1_OR_GREATER + ThreadPool.UnsafeQueueUserWorkItem(this, false); +#else + ThreadPool.UnsafeQueueUserWorkItem(s_Activate, this); +#endif + } + } + void IObserver.OnCompleted() + { + lock (_backlog) + { + Debug.WriteLine("Completing", ToString()); + _flags |= StateFlags.IsCompleted; + ActivateIfNeededLocked(); + } + } + + void IObserver.OnError(Exception error) + { + lock (_backlog) + { + Debug.WriteLine("Faulting", ToString()); + _fault = error; + _backlog.Clear(); // something bad happened; throw away the outstanding work + _flags |= StateFlags.IsCompleted; + ActivateIfNeededLocked(); + } + } + + void IObserver.OnNext(T value) + { + lock (_backlog) + { + if ((_flags & StateFlags.IsCompleted) == 0) + { + Debug.WriteLine($"Adding work: {value}", ToString()); + _backlog.Enqueue(value); + ActivateIfNeededLocked(); + } + else + { + Debug.WriteLine("Ignoring work: already completed", ToString()); + } + } + } + + ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) + => _pendingWork.GetStatus(token); + + void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) + => _pendingWork.OnCompleted(continuation, state, token, flags); + + bool IValueTaskSource.GetResult(short token) + { + lock (_backlog) + { + _pendingWork.GetResult(token); // discard the dummy value + _pendingWork.Reset(); + return _backlog.Count != 0; + } + } + } + /// /// Consumes the provided task raising exceptions as /// @@ -224,7 +484,7 @@ public static void UnarySyncVoid( { invoker.BlockingUnaryCall(method, host, context.CallOptions, request); } - catch(RpcException fault) + catch (RpcException fault) { metadata?.SetTrailers(fault); throw; @@ -310,9 +570,22 @@ public static IAsyncEnumerable ServerStreamingAsync method, TRequest request, string? host = null) where TRequest : class where TResponse : class - => ServerStreamingAsyncImpl(invoker.AsyncServerStreamingCall(method, host, context.CallOptions, request), context.Prepare(), context.CancellationToken); + => ServerStreamingAsyncImpl(invoker.AsyncServerStreamingCall(method, host, context.CallOptions, request), context.Prepare(), context.CancellationToken); + + /// + /// Performs a gRPC server-streaming call + /// + [Obsolete(WarningMessage, false)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IObservable ServerStreamingObservable( + this in CallContext context, + CallInvoker invoker, Method method, TRequest request, string? host = null) + where TRequest : class + where TResponse : class + => new ServerStreamingObservableImpl(invoker.AsyncServerStreamingCall(method, host, context.CallOptions, request), context.Prepare()); - private static async IAsyncEnumerable ServerStreamingAsyncImpl( + private static async IAsyncEnumerable ServerStreamingAsyncImpl( AsyncServerStreamingCall call, MetadataContext? metadata, [EnumeratorCancellation] CancellationToken cancellationToken) { @@ -350,6 +623,42 @@ private static async IAsyncEnumerable ServerStreamingAsyncImpl : ReaderObservable + { + private AsyncServerStreamingCall? _call; + private MetadataContext? _metadata; + public ServerStreamingObservableImpl(AsyncServerStreamingCall call, MetadataContext? metadata) : base(call.ResponseStream) + { + _call = call; + _metadata = metadata; + } + + public override void Dispose() + { + base.Dispose(); + var call = _call; + _call = null; + call?.Dispose(); + } + + protected override ValueTask OnBeforeAsync() + { + var metadata = _metadata; + return metadata is null ? default : metadata.SetHeadersAsync(_call?.ResponseHeadersAsync); + } + + protected override ValueTask OnAfterAsync() + { + _metadata?.SetTrailers(_call, c => c.GetStatus(), c => c.GetTrailers()); + return default; + } + + protected override void OnFault(RpcException fault) + { + _metadata?.SetTrailers(fault); + } + } + /// /// Performs a gRPC client-streaming call /// @@ -420,6 +729,71 @@ private static async Task ClientStreamingTaskAsyncImpl + /// Performs a gRPC client-streaming call + /// + [Obsolete(WarningMessage, false)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Task ClientStreamingObservableTaskAsync( + this in CallContext options, + CallInvoker invoker, Method method, IObservable request, string? host = null) + where TRequest : class + where TResponse : class + => ClientStreamingObservableTaskAsyncImpl(invoker.AsyncClientStreamingCall(method, host, options.CallOptions), options.Prepare(), request); + + /// + /// Performs a gRPC client-streaming call + /// + [Obsolete(WarningMessage, false)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueTask ClientStreamingObservableValueTaskAsync( + this in CallContext options, + CallInvoker invoker, Method method, IObservable request, string? host = null) + where TRequest : class + where TResponse : class + => new ValueTask(ClientStreamingObservableTaskAsyncImpl(invoker.AsyncClientStreamingCall(method, host, options.CallOptions), options.Prepare(), request)); + + /// + /// Performs a gRPC client-streaming call + /// + [Obsolete(WarningMessage, false)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueTask ClientStreamingObservableValueTaskAsyncVoid( + this in CallContext options, + CallInvoker invoker, Method method, IObservable request, string? host = null) + where TRequest : class + where TResponse : class + => new ValueTask(ClientStreamingObservableTaskAsyncImpl(invoker.AsyncClientStreamingCall(method, host, options.CallOptions), options.Prepare(), request)); + + private static async Task ClientStreamingObservableTaskAsyncImpl( + AsyncClientStreamingCall call, MetadataContext? metadata, + IObservable request) + { + using (call) + { + if (metadata != null) await metadata.SetHeadersAsync(call.ResponseHeadersAsync).ConfigureAwait(false); + + // send all the data *before* we check for a reply + try + { + await request.WriteObservableTo(call.RequestStream).ConfigureAwait(false); + + var result = await call.ResponseAsync.ConfigureAwait(false); + + metadata?.SetTrailers(call, c => c.GetStatus(), c => c.GetTrailers()); + return result; + } + catch (RpcException fault) + { + metadata?.SetTrailers(fault); + throw; + } + } + } + /// /// Performs a gRPC duplex call /// @@ -433,6 +807,19 @@ public static IAsyncEnumerable DuplexAsync( where TResponse : class => DuplexAsyncImpl(invoker.AsyncDuplexStreamingCall(method, host, options.CallOptions), options.Prepare(), options.IgnoreStreamTermination, request, options.CancellationToken); + /// + /// Performs a gRPC duplex call + /// + [Obsolete(WarningMessage, false)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IObservable DuplexObservable( + this in CallContext options, + CallInvoker invoker, Method method, IObservable request, string? host = null) + where TRequest : class + where TResponse : class + => new DuplexObservableImpl(invoker.AsyncDuplexStreamingCall(method, host, options.CallOptions), options.Prepare(), request); + private static async IAsyncEnumerable DuplexAsyncImpl( AsyncDuplexStreamingCall call, MetadataContext? metadata, bool ignoreStreamTermination, IAsyncEnumerable request, CancellationToken contextCancel, [EnumeratorCancellation] CancellationToken consumerCancel = default) @@ -495,6 +882,42 @@ private static async IAsyncEnumerable DuplexAsyncImpl : ReaderObservable + { + private AsyncDuplexStreamingCall? _call; + private MetadataContext? _metadata; + private readonly Task _sendAll; + public DuplexObservableImpl(AsyncDuplexStreamingCall call, MetadataContext? metadata, IObservable request) : base(call.ResponseStream) + { + _call = call; + _metadata = metadata; + _sendAll = request.WriteObservableTo(call.RequestStream); + } + + public override void Dispose() + { + base.Dispose(); + var call = _call; + _call = null; + call?.Dispose(); + } + + protected override ValueTask OnBeforeAsync() + { + var metadata = _metadata; + return metadata is null ? default : metadata.SetHeadersAsync(_call?.ResponseHeadersAsync); + } + + protected override async ValueTask OnAfterAsync() + { + await _sendAll; + _metadata?.SetTrailers(_call, c => c.GetStatus(), c => c.GetTrailers()); + } + + protected override void OnFault(RpcException fault) + => _metadata?.SetTrailers(fault); + } + static async Task SendAll(IClientStreamWriter output, IAsyncEnumerable request, CancellationTokenSource allDone, bool ignoreStreamTermination) { if (allDone.IsCancellationRequested) allDone.Token.ThrowIfCancellationRequested(); diff --git a/src/protobuf-net.Grpc/Internal/ServerInvokerLookup.cs b/src/protobuf-net.Grpc/Internal/ServerInvokerLookup.cs index 7ad5c6a6..8363c3e1 100644 --- a/src/protobuf-net.Grpc/Internal/ServerInvokerLookup.cs +++ b/src/protobuf-net.Grpc/Internal/ServerInvokerLookup.cs @@ -50,111 +50,154 @@ static Expression AsAsyncEnumerable(Expression value, Expression context) typeArguments: value.Type.GetGenericArguments(), arguments: new Expression[] { value, Expression.Property(context, nameof(ServerCallContext.CancellationToken)) }); + static Expression AsObservable(Expression value, Expression context) + => Expression.Call(typeof(Reshape), nameof(Reshape.AsObservable), + typeArguments: value.Type.GetGenericArguments(), + arguments: new Expression[] { value }); + static Expression WriteTo(Expression value, Expression writer, Expression context) => Expression.Call(typeof(Reshape), nameof(Reshape.WriteTo), typeArguments: value.Type.GetGenericArguments(), arguments: new Expression[] { value, writer, Expression.Property(context, nameof(ServerCallContext.CancellationToken)) }); - internal static bool TryGetValue(MethodType MethodType, ContextKind Context, ResultKind Result, VoidKind Void, out Func? invoker) - => _invokers.TryGetValue((MethodType, Context, Result, Void), out invoker); + static Expression WriteObservableTo(Expression value, Expression writer, Expression context) + => Expression.Call(typeof(Reshape), nameof(Reshape.WriteObservableTo), + typeArguments: value.Type.GetGenericArguments(), + arguments: new Expression[] { value, writer }); + + internal static bool TryGetValue(MethodType MethodType, ContextKind Context, ResultKind Arg, ResultKind Result, VoidKind Void, out Func? invoker) + => _invokers.TryGetValue((MethodType, Context, Arg, Result, Void), out invoker); #pragma warning restore CS0618 - private static readonly Dictionary<(MethodType Method, ContextKind Context, ResultKind Result, VoidKind Void), Func?> _invokers - = new Dictionary<(MethodType, ContextKind, ResultKind, VoidKind), Func?> + private static readonly Dictionary<(MethodType Method, ContextKind Context, ResultKind Arg,ResultKind Result, VoidKind Void), Func?> _invokers + = new Dictionary<(MethodType, ContextKind, ResultKind, ResultKind, VoidKind), Func?> { // GRPC-style server methods are direct match; no mapping required // => service.{method}(args) - { (MethodType.Unary, ContextKind.ServerCallContext, ResultKind.Task, VoidKind.None), null }, - { (MethodType.ServerStreaming, ContextKind.ServerCallContext, ResultKind.Task, VoidKind.None), null }, - { (MethodType.ClientStreaming, ContextKind.ServerCallContext, ResultKind.Task, VoidKind.None), null }, - { (MethodType.DuplexStreaming, ContextKind.ServerCallContext, ResultKind.Task, VoidKind.None), null }, + { (MethodType.Unary, ContextKind.ServerCallContext, ResultKind.Sync, ResultKind.Task, VoidKind.None), null }, + { (MethodType.ServerStreaming, ContextKind.ServerCallContext, ResultKind.Sync, ResultKind.Task, VoidKind.None), null }, + { (MethodType.ClientStreaming, ContextKind.ServerCallContext, ResultKind.Grpc, ResultKind.Task, VoidKind.None), null }, + { (MethodType.DuplexStreaming, ContextKind.ServerCallContext, ResultKind.Grpc, ResultKind.Task, VoidKind.None), null }, // Unary: Task Foo(TService service, TRequest request, ServerCallContext serverCallContext); // => service.{method}(request, [new CallContext(serverCallContext)]) - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Sync, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Sync, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Sync, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, // Unary: Task Foo(TService service, TRequest request, ServerCallContext serverCallContext); // => service.{method}(request, [new CallContext(serverCallContext)]) return Empty.Instance; - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Sync, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1])) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Sync, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Sync, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, args[1], ToCancellationToken(args[2]))) }, // Unary: Task Foo(TService service, TRequest request, ServerCallContext serverCallContext); // => service.{method}([new CallContext(serverCallContext)]) - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Task, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.ValueTask, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Task, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.ValueTask, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Task, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.ValueTask, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Task, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Sync, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Task, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Sync, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Task, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Sync, VoidKind.Request), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, // Unary: Task Foo(TService service, TRequest request, ServerCallContext serverCallContext); // => service.{method}([new CallContext(serverCallContext)]) return Empty.Instance; - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Task, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.ValueTask, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, - {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Task, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.ValueTask, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Task, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.ValueTask, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, - {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Task, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, + {(MethodType.Unary, ContextKind.NoContext, ResultKind.Sync, ResultKind.Sync, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method)) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Task, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CallContext, ResultKind.Sync, ResultKind.Sync, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCallContext(args[0], args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Task, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.ValueTask, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, + {(MethodType.Unary, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Sync, VoidKind.Both), (method, args) => ToTaskT(Expression.Call(args[0], method, ToCancellationToken(args[2]))) }, // Client Streaming: Task Foo(TService service, IAsyncStreamReader stream, ServerCallContext serverCallContext); // => service.{method}(reader.AsAsyncEnumerable(serverCallContext.CancellationToken), [new CallContext(serverCallContext)]) - {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, + + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + // and the same for observables + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.Observable, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.Observable, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, - {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[2]), ToCancellationToken(args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.Observable, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.Observable, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.Observable, ResultKind.Task, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCancellationToken(args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.Observable, ResultKind.ValueTask, VoidKind.None), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCancellationToken(args[2]))) }, + + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.Observable, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.NoContext, ResultKind.Observable, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]))) }, + + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.Observable, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CallContext, ResultKind.Observable, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCallContext(args[0], args[2]))) }, + + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.Observable, ResultKind.Task, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCancellationToken(args[2]))) }, + {(MethodType.ClientStreaming, ContextKind.CancellationToken, ResultKind.Observable, ResultKind.ValueTask, VoidKind.Response), (method, args) => ToTaskT(Expression.Call(args[0], method, AsObservable(args[1], args[2]), ToCancellationToken(args[2]))) }, // Server Streaming: Task Foo(TService service, TRequest request, IServerStreamWriter stream, ServerCallContext serverCallContext); // => service.{method}(request, [new CallContext(serverCallContext)]).WriteTo(stream, serverCallContext.CancellationToken) - {(MethodType.ServerStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, args[1]), args[2], args[3])}, - {(MethodType.ServerStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[3])), args[2], args[3])}, - {(MethodType.ServerStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, args[1], ToCancellationToken(args[3])), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.NoContext, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, args[1]), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CallContext, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[3])), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, args[1], ToCancellationToken(args[3])), args[2], args[3])}, + + {(MethodType.ServerStreaming, ContextKind.NoContext, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.Request), (method, args) => WriteTo(Expression.Call(args[0], method), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CallContext, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.Request), (method, args) => WriteTo(Expression.Call(args[0], method, ToCallContext(args[0], args[3])), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.AsyncEnumerable, VoidKind.Request), (method, args) => WriteTo(Expression.Call(args[0], method, ToCancellationToken(args[3])), args[2], args[3])}, + + // and the same for observables + {(MethodType.ServerStreaming, ContextKind.NoContext, ResultKind.Sync, ResultKind.Observable, VoidKind.None), (method, args) => WriteObservableTo(Expression.Call(args[0], method, args[1]), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CallContext, ResultKind.Sync, ResultKind.Observable, VoidKind.None), (method, args) => WriteObservableTo(Expression.Call(args[0], method, args[1], ToCallContext(args[0], args[3])), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Observable, VoidKind.None), (method, args) => WriteObservableTo(Expression.Call(args[0], method, args[1], ToCancellationToken(args[3])), args[2], args[3])}, - {(MethodType.ServerStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, VoidKind.Request), (method, args) => WriteTo(Expression.Call(args[0], method), args[2], args[3])}, - {(MethodType.ServerStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, VoidKind.Request), (method, args) => WriteTo(Expression.Call(args[0], method, ToCallContext(args[0], args[3])), args[2], args[3])}, - {(MethodType.ServerStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, VoidKind.Request), (method, args) => WriteTo(Expression.Call(args[0], method, ToCancellationToken(args[3])), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.NoContext, ResultKind.Sync, ResultKind.Observable, VoidKind.Request), (method, args) => WriteObservableTo(Expression.Call(args[0], method), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CallContext, ResultKind.Sync, ResultKind.Observable, VoidKind.Request), (method, args) => WriteObservableTo(Expression.Call(args[0], method, ToCallContext(args[0], args[3])), args[2], args[3])}, + {(MethodType.ServerStreaming, ContextKind.CancellationToken, ResultKind.Sync, ResultKind.Observable, VoidKind.Request), (method, args) => WriteObservableTo(Expression.Call(args[0], method, ToCancellationToken(args[3])), args[2], args[3])}, // Duplex: Task Foo(TService service, IAsyncStreamReader input, IServerStreamWriter output, ServerCallContext serverCallContext); // => service.{method}(input.AsAsyncEnumerable(serverCallContext.CancellationToken), [new CallContext(serverCallContext)]).WriteTo(output, serverCallContext.CancellationToken) - {(MethodType.DuplexStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[3])), args[2], args[3]) }, - {(MethodType.DuplexStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[3]), ToCallContext(args[0], args[3])), args[2], args[3]) }, - {(MethodType.DuplexStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[3]), ToCancellationToken(args[3])), args[2], args[3]) }, + {(MethodType.DuplexStreaming, ContextKind.NoContext, ResultKind.AsyncEnumerable, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[3])), args[2], args[3]) }, + {(MethodType.DuplexStreaming, ContextKind.CallContext, ResultKind.AsyncEnumerable, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[3]), ToCallContext(args[0], args[3])), args[2], args[3]) }, + {(MethodType.DuplexStreaming, ContextKind.CancellationToken, ResultKind.AsyncEnumerable, ResultKind.AsyncEnumerable, VoidKind.None), (method, args) => WriteTo(Expression.Call(args[0], method, AsAsyncEnumerable(args[1], args[3]), ToCancellationToken(args[3])), args[2], args[3]) }, + + // and for observables + {(MethodType.DuplexStreaming, ContextKind.NoContext, ResultKind.Observable, ResultKind.Observable, VoidKind.None), (method, args) => WriteObservableTo(Expression.Call(args[0], method, AsObservable(args[1], args[3])), args[2], args[3]) }, + {(MethodType.DuplexStreaming, ContextKind.CallContext, ResultKind.Observable,ResultKind.Observable, VoidKind.None), (method, args) => WriteObservableTo(Expression.Call(args[0], method, AsObservable(args[1], args[3]), ToCallContext(args[0], args[3])), args[2], args[3]) }, + {(MethodType.DuplexStreaming, ContextKind.CancellationToken, ResultKind.Observable, ResultKind.Observable, VoidKind.None), (method, args) => WriteObservableTo(Expression.Call(args[0], method, AsObservable(args[1], args[3]), ToCancellationToken(args[3])), args[2], args[3]) }, }; } } diff --git a/tests/protobuf-net.Grpc.Test.Integration/StreamTests.cs b/tests/protobuf-net.Grpc.Test.Integration/StreamTests.cs index afdf1fd4..d5d8768b 100644 --- a/tests/protobuf-net.Grpc.Test.Integration/StreamTests.cs +++ b/tests/protobuf-net.Grpc.Test.Integration/StreamTests.cs @@ -7,7 +7,10 @@ using ProtoBuf.Meta; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -29,7 +32,7 @@ public void Log(string message) { lock (tmp) { - tmp.WriteLine(message); + tmp.WriteLine(DateTime.Now + ":" + message); } } } @@ -56,6 +59,7 @@ public void Init() public interface IStreamAPI { IAsyncEnumerable DuplexEcho(IAsyncEnumerable values, CallContext ctx = default); + IObservable DuplexEchoObservable(IObservable values, CallContext ctx = default); IAsyncEnumerable FullDuplex(IAsyncEnumerable values, CallContext ctx = default); @@ -89,7 +93,11 @@ class StreamServer : IStreamAPI readonly StreamTestsFixture _fixture; internal StreamServer(StreamTestsFixture fixture) => _fixture = fixture; - public void Log(string message) => _fixture.Log(message); + public void Log(string message) + { + Debug.WriteLine(message); + _fixture.Log(message); + } static Scenario GetScenario(in CallContext ctx) { @@ -160,6 +168,81 @@ async IAsyncEnumerable IStreamAPI.DuplexEcho(IAsyncEnumerable values, Log("server is complete"); } + IObservable IStreamAPI.DuplexEchoObservable(IObservable values, CallContext ctx) + { + Log("server checking scenario"); + var scenario = GetScenario(ctx); + + if (scenario == Scenario.FaultBeforeHeaders) Throw("before headers"); + + var sCtx = ctx.ServerCallContext!; + Log("server yielding response headers"); + sCtx.WriteResponseHeadersAsync(new Metadata { { "prekey", "preval" } }).Wait(); // sync-over-async; bite me + + Log("server setting response status in advance"); + sCtx.Status = new Status(StatusCode.OK, "resp detail"); + sCtx.ResponseTrailers.Add("postkey", "postval"); + + if (scenario == Scenario.FaultBeforeYield) Throw("before yield"); + + switch (scenario) + { + case Scenario.FaultSuccessBadProducer: + case Scenario.FaultSuccessGoodProducer: + var prefaulted = new Subject(); + try + { + Log("server faulting with success"); + throw new RpcException(Status.DefaultSuccess); // another way of expressing yield break + } + catch (Exception ex) + { + prefaulted.OnError(ex); + } + return prefaulted; + case Scenario.TakeNothingBadProducer: + case Scenario.TakeNothingGoodProducer: + var empty = new Subject(); + empty.OnCompleted(); + return empty; + default: + var subject = new Subject(); + values.Subscribe(value => + { + Log($"server received {value.Bar}"); + switch (scenario) + { + case Scenario.YieldNothing: + break; + default: + Log($"server yielding {value.Bar}"); + subject.OnNext(value); + break; + } + }, () => + { + Log("server is done yielding"); + if (scenario == Scenario.FaultAfterYield) + { + try + { + Throw("after yield"); + } + catch (Exception ex) + { + subject.OnError(ex); + } + } + else + { + subject.OnCompleted(); + } + Log("server is complete"); + }); + return subject; + } + } + static void Throw(string state) => throw new RpcException(new Status(StatusCode.Internal, state + " detail"), new Metadata { { "faultkey", state + " faultval" } }, state + " message"); @@ -473,6 +556,89 @@ async Task CheckHeaderStateAsync() } } + + [DebugTheory] + [InlineData(Scenario.RunToCompletion, DEFAULT_SIZE, CallContextFlags.None)] + [InlineData(Scenario.RunToCompletion, DEFAULT_SIZE, CallContextFlags.CaptureMetadata)] + + [InlineData(Scenario.YieldNothing, 0, CallContextFlags.IgnoreStreamTermination)] + [InlineData(Scenario.YieldNothing, 0, CallContextFlags.IgnoreStreamTermination | CallContextFlags.CaptureMetadata)] + [InlineData(Scenario.YieldNothing, 0, CallContextFlags.None)] + [InlineData(Scenario.YieldNothing, 0, CallContextFlags.CaptureMetadata)] + //[InlineData(Scenario.TakeNothingGoodProducer, 0, CallContextFlags.IgnoreStreamTermination)] + //[InlineData(Scenario.TakeNothingGoodProducer, 0, CallContextFlags.IgnoreStreamTermination | CallContextFlags.CaptureMetadata)] + //[InlineData(Scenario.TakeNothingGoodProducer, 0, CallContextFlags.None)] + //[InlineData(Scenario.TakeNothingGoodProducer, 0, CallContextFlags.CaptureMetadata)] + //[InlineData(Scenario.FaultSuccessGoodProducer, 0, CallContextFlags.IgnoreStreamTermination)] + //[InlineData(Scenario.FaultSuccessGoodProducer, 0, CallContextFlags.IgnoreStreamTermination | CallContextFlags.CaptureMetadata)] + //[InlineData(Scenario.FaultSuccessGoodProducer, 0, CallContextFlags.None)] + //[InlineData(Scenario.FaultSuccessGoodProducer, 0, CallContextFlags.CaptureMetadata)] + //[InlineData(Scenario.TakeNothingBadProducer, 0, CallContextFlags.IgnoreStreamTermination)] + //[InlineData(Scenario.TakeNothingBadProducer, 0, CallContextFlags.IgnoreStreamTermination | CallContextFlags.CaptureMetadata)] + //[InlineData(Scenario.FaultSuccessBadProducer, 0, CallContextFlags.IgnoreStreamTermination)] + //[InlineData(Scenario.FaultSuccessBadProducer, 0, CallContextFlags.IgnoreStreamTermination | CallContextFlags.CaptureMetadata)] + + //[InlineData(Scenario.TakeNothingBadProducer, 0, CallContextFlags.None, true)] + //[InlineData(Scenario.TakeNothingBadProducer, 0, CallContextFlags.CaptureMetadata, true)] + //[InlineData(Scenario.FaultSuccessBadProducer, 0, CallContextFlags.None)] + //[InlineData(Scenario.FaultSuccessBadProducer, 0, CallContextFlags.CaptureMetadata)] + public async Task DuplexEchoObservable(Scenario scenario, int expectedCount, CallContextFlags flags, bool expectBrittle = false) + { + await using var svc = CreateClient(out var client); + + var ctx = new CallContext(new CallOptions(deadline: DateTime.UtcNow.AddSeconds(20), headers: new Metadata { { nameof(Scenario), scenario.ToString() } }), flags); + + bool haveCheckedHeaders = false; + var values = new List(expectedCount); + try + { + await client.DuplexEchoObservable(ForObservable(DEFAULT_SIZE), ctx).ForEachAsync(async item => + { + _fixture.Log($"client received {item.Bar}"); + await CheckHeaderStateAsync(); + values.Add(item.Bar); + }); + } + catch (Exception ex) when (expectBrittle && ex.GetType().FullName == "ProtoBuf.Grpc.Internal.IncompleteSendRpcException") + { + _fixture?.Log($"faulted as incomplete; user advised: '{ex.Message}'"); + return; // best we can do + } + _fixture?.Log("after await foreach"); + await CheckHeaderStateAsync(); + Assert.Equal(string.Join(",", Enumerable.Range(0, expectedCount)), string.Join(",", values)); + + if ((flags & CallContextFlags.CaptureMetadata) != 0) + { // check trailers + Assert.Equal("postval", ctx.ResponseTrailers().GetString("postkey")); + + var status = ctx.ResponseStatus(); + Assert.Equal(StatusCode.OK, status.StatusCode); + switch (scenario) + { + case Scenario.FaultSuccessGoodProducer: + case Scenario.FaultSuccessBadProducer: + Assert.Equal("", status.Detail); + break; + default: + Assert.Equal("resp detail", status.Detail); + break; + } + } + + async Task CheckHeaderStateAsync() + { + if (haveCheckedHeaders) return; + haveCheckedHeaders = true; + if ((flags & CallContextFlags.CaptureMetadata) != 0) + { + var headers = await ctx.ResponseHeadersAsync(); + var prekey = headers.GetString("prekey"); + Assert.Equal("preval", prekey); + } + } + } + [DebugTheory] [InlineData(Scenario.FaultAfterYield, DEFAULT_SIZE, "after yield", CallContextFlags.None)] [InlineData(Scenario.FaultAfterYield, DEFAULT_SIZE, "after yield", CallContextFlags.CaptureMetadata)] @@ -541,6 +707,10 @@ IAsyncEnumerable For(Scenario scenario, int count, int from = 0, int millis Scenario.TakeNothingBadProducer => false, _ => true }, default); + + IObservable ForObservable(int count, int from = 0, int millisecondsDelay = 10) + => ForObservableImpl(_fixture, count, from, millisecondsDelay); + private static async IAsyncEnumerable ForImpl(StreamTestsFixture fixture, int count, int from, int millisecondsDelay, bool checkForCancellation, [EnumeratorCancellation] CancellationToken cancellationToken) { @@ -576,6 +746,43 @@ void CheckForCancellation(string when) } } + private static IObservable ForObservableImpl(StreamTestsFixture fixture, int count, int from, int millisecondsDelay) + { + void Log(string message) + { + Debug.WriteLine(message); + fixture?.Log(message); + } + + var subject = new Subject(); + Task.Run(async () => + { + Log("starting producer"); + try + { + for (int i = 0; i < count; i++) + { + await Task.Delay(millisecondsDelay); + + Log($"producer yielding {i}"); + subject.OnNext(new Foo { Bar = i + from }); + } + + Log($"producer ran to completion"); + subject.OnCompleted(); + } + catch (Exception ex) + { + subject.OnError(ex); + } + finally + { + Log("exiting producer"); + } + }); + return subject; + } + [DebugTheory] [InlineData(Scenario.FaultBeforeHeaders, CallContextFlags.None)] [InlineData(Scenario.FaultBeforeHeaders, CallContextFlags.CaptureMetadata)] diff --git a/tests/protobuf-net.Grpc.Test.Integration/protobuf-net.Grpc.Test.Integration.csproj b/tests/protobuf-net.Grpc.Test.Integration/protobuf-net.Grpc.Test.Integration.csproj index 8c7106f1..9d552bc2 100644 --- a/tests/protobuf-net.Grpc.Test.Integration/protobuf-net.Grpc.Test.Integration.csproj +++ b/tests/protobuf-net.Grpc.Test.Integration/protobuf-net.Grpc.Test.Integration.csproj @@ -9,6 +9,7 @@ + all diff --git a/tests/protobuf-net.Grpc.Test.IntegrationUpLevel/protobuf-net.Grpc.Test.IntegrationUpLevel.csproj b/tests/protobuf-net.Grpc.Test.IntegrationUpLevel/protobuf-net.Grpc.Test.IntegrationUpLevel.csproj index f51d955d..5cd6a401 100644 --- a/tests/protobuf-net.Grpc.Test.IntegrationUpLevel/protobuf-net.Grpc.Test.IntegrationUpLevel.csproj +++ b/tests/protobuf-net.Grpc.Test.IntegrationUpLevel/protobuf-net.Grpc.Test.IntegrationUpLevel.csproj @@ -11,6 +11,7 @@ + all diff --git a/tests/protobuf-net.Grpc.Test/ContractOperationTests.cs b/tests/protobuf-net.Grpc.Test/ContractOperationTests.cs index 2e097cf0..20d544d9 100644 --- a/tests/protobuf-net.Grpc.Test/ContractOperationTests.cs +++ b/tests/protobuf-net.Grpc.Test/ContractOperationTests.cs @@ -52,13 +52,13 @@ public void SublclassInterfaces() [Fact] public void GeneralPurposeSignatureCount() { - Assert.Equal(57, ContractOperation.GeneralPurposeSignatureCount()); + Assert.Equal(78, ContractOperation.GeneralPurposeSignatureCount()); } [Fact] public void ServerSignatureCount() { - Assert.Equal(57, ServerInvokerLookup.GeneralPurposeSignatureCount()); + Assert.Equal(78, ServerInvokerLookup.GeneralPurposeSignatureCount()); } [Fact] @@ -84,81 +84,107 @@ public void CheckAllMethodsCovered() } #pragma warning disable CS0618 // Empty [Theory] - [InlineData(nameof(IAllOptions.Client_AsyncUnary), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Client_BlockingUnary), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallOptions, (int)ResultKind.Sync, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Client_ClientStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Client_Duplex), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Client_ServerStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None)] - - [InlineData(nameof(IAllOptions.Server_ClientStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Server_Duplex), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Server_ServerStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Server_Unary), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None)] - - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.Response)] - - [InlineData(nameof(IAllOptions.Shared_Duplex_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CallContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_Duplex_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.NoContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_Duplex_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None)] - - [InlineData(nameof(IAllOptions.Shared_ServerStreaming_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ServerStreaming_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.NoContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ServerStreaming_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ServerStreaming_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_ServerStreaming_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.NoContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_ServerStreaming_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.AsyncEnumerable, (int)VoidKind.Request)] - - [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Response)] - - [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Response)] - - [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Response)] - - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.None)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Both)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Request)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Response)] - [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Response)] - public void CheckMethodIdentification(string name, Type from, Type to, MethodType methodType, int context, int result, int @void) + [InlineData(nameof(IAllOptions.Client_AsyncUnary), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Client_BlockingUnary), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallOptions, (int)ResultKind.Sync, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Client_ClientStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None, (int)ResultKind.Grpc)] + [InlineData(nameof(IAllOptions.Client_Duplex), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None, (int)ResultKind.Grpc)] + [InlineData(nameof(IAllOptions.Client_ServerStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallOptions, (int)ResultKind.Grpc, (int)VoidKind.None, (int)ResultKind.Sync)] + + [InlineData(nameof(IAllOptions.Server_ClientStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Grpc)] + [InlineData(nameof(IAllOptions.Server_Duplex), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Grpc)] + [InlineData(nameof(IAllOptions.Server_ServerStreaming), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Server_Unary), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.ServerCallContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Sync)] + + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Sync, (int)VoidKind.Response, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Sync, (int)VoidKind.Response, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_BlockingUnary_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Sync, (int)VoidKind.Response, (int)ResultKind.Sync)] + + [InlineData(nameof(IAllOptions.Shared_Duplex_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CallContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_Duplex_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.NoContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_Duplex_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.NoContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.AsyncEnumerable, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.NoContext, (int)ResultKind.AsyncEnumerable, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.AsyncEnumerable, (int)VoidKind.Request, (int)ResultKind.Sync)] + + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.AsyncEnumerable)] + + [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_TaskUnary_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.Sync)] + + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.AsyncEnumerable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.AsyncEnumerable)] + + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken), typeof(HelloRequest), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken_VoidVoid), typeof(Empty), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Both, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken_VoidVal), typeof(Empty), typeof(HelloReply), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_Context_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_NoContext_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskUnary_CancellationToken_ValVoid), typeof(HelloRequest), typeof(Empty), MethodType.Unary, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.Sync)] + + // observable + [InlineData(nameof(IAllOptions.Shared_Duplex_Context_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CallContext, (int)ResultKind.Observable, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_Duplex_NoContext_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.NoContext, (int)ResultKind.Observable, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_Duplex_CancellationToken_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.DuplexStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Observable, (int)VoidKind.None, (int)ResultKind.Observable)] + + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_Context_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallContext, (int)ResultKind.Observable, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_NoContext_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.NoContext, (int)ResultKind.Observable, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_CancellationToken_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Observable, (int)VoidKind.None, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_Context_VoidVal_Observable), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CallContext, (int)ResultKind.Observable, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_NoContext_VoidVal_Observable), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.NoContext, (int)ResultKind.Observable, (int)VoidKind.Request, (int)ResultKind.Sync)] + [InlineData(nameof(IAllOptions.Shared_ServerStreaming_CancellationToken_VoidVal_Observable), typeof(Empty), typeof(HelloReply), MethodType.ServerStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Observable, (int)VoidKind.Request, (int)ResultKind.Sync)] + + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_Context_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_NoContext_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_CancellationToken_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_Context_ValVoid_Observable), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_NoContext_ValVoid_Observable), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_TaskClientStreaming_CancellationToken_ValVoid_Observable), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.Task, (int)VoidKind.Response, (int)ResultKind.Observable)] + + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_Context_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_NoContext_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_CancellationToken_Observable), typeof(HelloRequest), typeof(HelloReply), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.None, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_Context_ValVoid_Observable), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CallContext, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_NoContext_ValVoid_Observable), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.NoContext, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.Observable)] + [InlineData(nameof(IAllOptions.Shared_ValueTaskClientStreaming_CancellationToken_ValVoid_Observable), typeof(HelloRequest), typeof(Empty), MethodType.ClientStreaming, (int)ContextKind.CancellationToken, (int)ResultKind.ValueTask, (int)VoidKind.Response, (int)ResultKind.Observable)] + public void CheckMethodIdentification(string name, Type from, Type to, MethodType methodType, int context, int result, int @void, int arg) { var method = typeof(IAllOptions).GetMethod(name); Assert.NotNull(method); @@ -172,6 +198,7 @@ public void CheckMethodIdentification(string name, Type from, Type to, MethodTyp Assert.Equal(methodType, operation.MethodType); Assert.Equal((ContextKind)context, operation.Context); Assert.Equal(method!.Name, operation.Name); + Assert.Equal((ResultKind)arg, operation.Arg); Assert.Equal((ResultKind)result, operation.Result); Assert.Equal(from, operation.From); Assert.Equal(to, operation.To); diff --git a/tests/protobuf-net.Grpc.Test/IAllOptions.cs b/tests/protobuf-net.Grpc.Test/IAllOptions.cs index b7d1d20f..6308e423 100644 --- a/tests/protobuf-net.Grpc.Test/IAllOptions.cs +++ b/tests/protobuf-net.Grpc.Test/IAllOptions.cs @@ -1,6 +1,7 @@ using Grpc.Core; using ProtoBuf; using ProtoBuf.Grpc; +using System; using System.Collections.Generic; using System.ServiceModel; using System.Threading; @@ -116,5 +117,33 @@ interface IAllOptions IAsyncEnumerable Shared_Duplex_NoContext(IAsyncEnumerable request); IAsyncEnumerable Shared_Duplex_Context(IAsyncEnumerable request, CallContext context); IAsyncEnumerable Shared_Duplex_CancellationToken(IAsyncEnumerable request, CancellationToken cancellationToken); + + // client-streaming (observable) + Task Shared_TaskClientStreaming_NoContext_Observable(IObservable request); + Task Shared_TaskClientStreaming_Context_Observable(IObservable request, CallContext context); + Task Shared_TaskClientStreaming_CancellationToken_Observable(IObservable request, CancellationToken cancellationToken); + Task Shared_TaskClientStreaming_NoContext_ValVoid_Observable(IObservable request); + Task Shared_TaskClientStreaming_Context_ValVoid_Observable(IObservable request, CallContext context); + Task Shared_TaskClientStreaming_CancellationToken_ValVoid_Observable(IObservable request, CancellationToken cancellationToken); + + ValueTask Shared_ValueTaskClientStreaming_NoContext_Observable(IObservable request); + ValueTask Shared_ValueTaskClientStreaming_Context_Observable(IObservable request, CallContext context); + ValueTask Shared_ValueTaskClientStreaming_CancellationToken_Observable(IObservable request, CancellationToken cancellationToken); + ValueTask Shared_ValueTaskClientStreaming_NoContext_ValVoid_Observable(IObservable request); + ValueTask Shared_ValueTaskClientStreaming_Context_ValVoid_Observable(IObservable request, CallContext context); + ValueTask Shared_ValueTaskClientStreaming_CancellationToken_ValVoid_Observable(IObservable request, CancellationToken cancellationToken); + + // server-streaming (observable) + IObservable Shared_ServerStreaming_NoContext_Observable(HelloRequest request); + IObservable Shared_ServerStreaming_Context_Observable(HelloRequest request, CallContext context); + IObservable Shared_ServerStreaming_CancellationToken_Observable(HelloRequest request, CancellationToken cancellationToken); + IObservable Shared_ServerStreaming_NoContext_VoidVal_Observable(); + IObservable Shared_ServerStreaming_Context_VoidVal_Observable(CallContext context); + IObservable Shared_ServerStreaming_CancellationToken_VoidVal_Observable(CancellationToken cancellationToken); + + // duplex (observable) + IObservable Shared_Duplex_NoContext_Observable(IObservable request); + IObservable Shared_Duplex_Context_Observable(IObservable request, CallContext context); + IObservable Shared_Duplex_CancellationToken_Observable(IObservable request, CancellationToken cancellationToken); } }