< Summary

Information
Class: IceRpc.Slice.Operations.SliceProxyExtensions
Assembly: IceRpc.Slice
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Slice/Operations/SliceProxyExtensions.cs
Tag: 1856_27024993493
Line coverage
82%
Covered lines: 60
Uncovered lines: 13
Coverable lines: 73
Total lines: 183
Line coverage: 82.1%
Branch coverage
50%
Covered branches: 6
Total branches: 12
Branch coverage: 50%
Method coverage
80%
Covered methods: 4
Fully covered methods: 2
Total methods: 5
Method coverage: 80%
Full method coverage: 40%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
InvokeOperationAsync(...)50%7671.42%
ReadResponseAsync()100%11100%
InvokeOperationAsync(...)50%6690.9%
ReadResponseAsync()100%11100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Slice/Operations/SliceProxyExtensions.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Features;
 4using System.Collections.Immutable;
 5using System.IO.Pipelines;
 6
 7namespace IceRpc.Slice.Operations;
 8
 9/// <summary>Represents a delegate that decodes the return value from a Slice-encoded response.</summary>
 10/// <typeparam name="T">The type of the return value to read.</typeparam>
 11/// <param name="response">The incoming response.</param>
 12/// <param name="request">The outgoing request.</param>
 13/// <param name="sender">The proxy that sent the request.</param>
 14/// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 15/// <returns>A value task that contains the decoded return value.</returns>
 16public delegate ValueTask<T> ResponseDecodeFunc<T>(
 17    IncomingResponse response,
 18    OutgoingRequest request,
 19    ISliceProxy sender,
 20    CancellationToken cancellationToken);
 21
 22/// <summary>Represents a delegate that decodes the "void" return value from a Slice-encoded response.</summary>
 23/// <param name="response">The incoming response.</param>
 24/// <param name="request">The outgoing request.</param>
 25/// <param name="sender">The proxy that sent the request.</param>
 26/// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 27/// <returns>A value task.</returns>
 28public delegate ValueTask ResponseDecodeFunc(
 29    IncomingResponse response,
 30    OutgoingRequest request,
 31    ISliceProxy sender,
 32    CancellationToken cancellationToken);
 33
 34/// <summary>Provides extension methods for <see cref="ISliceProxy" /> and generated proxy structs that implement this
 35/// interface.</summary>
 36public static class SliceProxyExtensions
 37{
 038    private static readonly IDictionary<RequestFieldKey, OutgoingFieldValue> _idempotentFields =
 039        new Dictionary<RequestFieldKey, OutgoingFieldValue>
 040        {
 041            [RequestFieldKey.Idempotent] = default
 042        }.ToImmutableDictionary();
 43
 44    /// <summary>Sends a request to a service and decodes the response.</summary>
 45    /// <typeparam name="TProxy">The type of the proxy struct.</typeparam>
 46    /// <typeparam name="T">The response type.</typeparam>
 47    /// <param name="proxy">A proxy to the remote service.</param>
 48    /// <param name="operation">The name of the operation, as specified in Slice.</param>
 49    /// <param name="payload">The payload of the request.</param>
 50    /// <param name="payloadContinuation">The optional payload continuation of the request.</param>
 51    /// <param name="responseDecodeFunc">The decode function for the response payload.</param>
 52    /// <param name="features">The invocation features.</param>
 53    /// <param name="idempotent">When <see langword="true" />, the request is idempotent.</param>
 54    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 55    /// <returns>The operation's return value.</returns>
 56    public static Task<T> InvokeOperationAsync<TProxy, T>(
 57        this TProxy proxy,
 58        string operation,
 59        PipeReader payload,
 60        PipeReader? payloadContinuation,
 61        ResponseDecodeFunc<T> responseDecodeFunc,
 62        IFeatureCollection? features = null,
 63        bool idempotent = false,
 64        CancellationToken cancellationToken = default) where TProxy : struct, ISliceProxy
 2865    {
 2866        if (proxy.Invoker is not IInvoker invoker)
 067        {
 068            throw new InvalidOperationException("Cannot send requests using a proxy with a null invoker.");
 69        }
 70
 2871        var request = new OutgoingRequest(proxy.ServiceAddress)
 2872        {
 2873            Features = features ?? FeatureCollection.Empty,
 2874            Fields = idempotent ? _idempotentFields : ImmutableDictionary<RequestFieldKey, OutgoingFieldValue>.Empty,
 2875            Operation = operation,
 2876            Payload = payload,
 2877            PayloadContinuation = payloadContinuation
 2878        };
 79
 80        Task<IncomingResponse> responseTask;
 81        try
 2882        {
 2883            responseTask = invoker.InvokeAsync(request, cancellationToken);
 2884        }
 085        catch
 086        {
 087            request.Dispose();
 088            throw;
 89        }
 90
 91        // ReadResponseAsync is responsible for disposing the request
 2892        return ReadResponseAsync(responseTask, request);
 93
 94        async Task<T> ReadResponseAsync(Task<IncomingResponse> responseTask, OutgoingRequest request)
 2895        {
 96            try
 2897            {
 2898                IncomingResponse response = await responseTask.ConfigureAwait(false);
 2899                return await responseDecodeFunc(
 28100                    response,
 28101                    request,
 28102                    proxy,
 28103                    cancellationToken).ConfigureAwait(false);
 104            }
 105            finally
 28106            {
 28107                request.Dispose();
 28108            }
 28109        }
 28110    }
 111
 112    /// <summary>Sends a request to a service and decodes the "void" response.</summary>
 113    /// <typeparam name="TProxy">The type of the proxy struct.</typeparam>
 114    /// <param name="proxy">A proxy for the remote service.</param>
 115    /// <param name="operation">The name of the operation, as specified in Slice.</param>
 116    /// <param name="payload">The payload of the request.</param>
 117    /// <param name="payloadContinuation">The payload continuation of the request.</param>
 118    /// <param name="responseDecodeFunc">The decode function for the response payload.</param>
 119    /// <param name="features">The invocation features.</param>
 120    /// <param name="idempotent">When <see langword="true" />, the request is idempotent.</param>
 121    /// <param name="oneway">When <see langword="true" />, the request is sent one-way and an empty response is returned
 122    /// immediately after sending the request.</param>
 123    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 124    /// <returns>A task that completes when the void response is returned.</returns>
 125    public static Task InvokeOperationAsync<TProxy>(
 126        this TProxy proxy,
 127        string operation,
 128        PipeReader payload,
 129        PipeReader? payloadContinuation,
 130        ResponseDecodeFunc responseDecodeFunc,
 131        IFeatureCollection? features = null,
 132        bool idempotent = false,
 133        bool oneway = false,
 134        CancellationToken cancellationToken = default) where TProxy : struct, ISliceProxy
 21135    {
 21136        if (proxy.Invoker is not IInvoker invoker)
 0137        {
 0138            throw new InvalidOperationException("Cannot send requests using a proxy with a null invoker.");
 139        }
 140
 21141        var request = new OutgoingRequest(proxy.ServiceAddress)
 21142        {
 21143            Features = features ?? FeatureCollection.Empty,
 21144            Fields = idempotent ? _idempotentFields : ImmutableDictionary<RequestFieldKey, OutgoingFieldValue>.Empty,
 21145            IsOneway = oneway,
 21146            Operation = operation,
 21147            Payload = payload,
 21148            PayloadContinuation = payloadContinuation
 21149        };
 150
 151        Task<IncomingResponse> responseTask;
 152        try
 21153        {
 21154            responseTask = invoker.InvokeAsync(request, cancellationToken);
 20155        }
 1156        catch
 1157        {
 1158            request.Dispose();
 1159            throw;
 160        }
 161
 162        // ReadResponseAsync is responsible for disposing the request
 20163        return ReadResponseAsync(responseTask, request);
 164
 165        async Task ReadResponseAsync(Task<IncomingResponse> responseTask, OutgoingRequest request)
 20166        {
 167            try
 20168            {
 20169                IncomingResponse response = await responseTask.ConfigureAwait(false);
 170
 20171                await responseDecodeFunc(
 20172                    response,
 20173                    request,
 20174                    proxy,
 20175                    cancellationToken).ConfigureAwait(false);
 20176            }
 177            finally
 20178            {
 20179                request.Dispose();
 20180            }
 20181        }
 20182    }
 183}