< Summary

Information
Class: IceRpc.Slice.ProxyExtensions
Assembly: IceRpc.Slice
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Slice/ProxyExtensions.cs
Tag: 701_22528036593
Line coverage
89%
Covered lines: 66
Uncovered lines: 8
Coverable lines: 74
Total lines: 198
Line coverage: 89.1%
Branch coverage
83%
Covered branches: 10
Total branches: 12
Branch coverage: 83.3%
Method coverage
100%
Covered methods: 6
Total methods: 6
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
InvokeOperationAsync(...)83.33%6.84671.42%
ReadResponseAsync()100%11100%
InvokeOperationAsync(...)83.33%6.03690.9%
ReadResponseAsync()100%11100%
ToProxy(...)100%11100%

File(s)

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

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Features;
 4using IceRpc.Internal;
 5using System.Collections.Immutable;
 6using System.IO.Pipelines;
 7using ZeroC.Slice;
 8
 9namespace IceRpc.Slice;
 10
 11/// <summary>Represents a delegate that decodes the return value from a Slice-encoded response.</summary>
 12/// <typeparam name="T">The type of the return value to read.</typeparam>
 13/// <param name="response">The incoming response.</param>
 14/// <param name="request">The outgoing request.</param>
 15/// <param name="sender">The proxy that sent the request.</param>
 16/// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 17/// <returns>A value task that contains the return value or a <see cref="SliceException" /> when the status code of the
 18/// response is <see cref="StatusCode.ApplicationError" />.</returns>
 19public delegate ValueTask<T> ResponseDecodeFunc<T>(
 20    IncomingResponse response,
 21    OutgoingRequest request,
 22    IProxy sender,
 23    CancellationToken cancellationToken);
 24
 25/// <summary>Represents a delegate that decodes the "void" return value from a Slice-encoded response.</summary>
 26/// <param name="response">The incoming response.</param>
 27/// <param name="request">The outgoing request.</param>
 28/// <param name="sender">The proxy that sent the request.</param>
 29/// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 30/// <returns>A value task that contains a <see cref="SliceException" /> when the status code of the response is
 31/// <see cref="StatusCode.ApplicationError" />.</returns>
 32public delegate ValueTask ResponseDecodeFunc(
 33    IncomingResponse response,
 34    OutgoingRequest request,
 35    IProxy sender,
 36    CancellationToken cancellationToken);
 37
 38/// <summary>Provides extension methods for <see cref="IProxy" /> and generated proxy structs that implement this
 39/// interface.</summary>
 40public static class ProxyExtensions
 41{
 242    private static readonly IDictionary<RequestFieldKey, OutgoingFieldValue> _idempotentFields =
 243        new Dictionary<RequestFieldKey, OutgoingFieldValue>
 244        {
 245            [RequestFieldKey.Idempotent] = default
 246        }.ToImmutableDictionary();
 47
 48    /// <summary>Sends a request to a service and decodes the response.</summary>
 49    /// <typeparam name="TProxy">The type of the proxy struct.</typeparam>
 50    /// <typeparam name="T">The response type.</typeparam>
 51    /// <param name="proxy">A proxy to the remote service.</param>
 52    /// <param name="operation">The name of the operation, as specified in Slice.</param>
 53    /// <param name="payload">The payload of the request.</param>
 54    /// <param name="payloadContinuation">The optional payload continuation of the request.</param>
 55    /// <param name="responseDecodeFunc">The decode function for the response payload. It decodes and throws an
 56    /// exception when the status code of the response is <see cref="StatusCode.ApplicationError" />.</param>
 57    /// <param name="features">The invocation features.</param>
 58    /// <param name="idempotent">When <see langword="true" />, the request is idempotent.</param>
 59    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 60    /// <returns>The operation's return value.</returns>
 61    /// <exception cref="SliceException">Thrown if the response carries a Slice exception.</exception>
 62    public static Task<T> InvokeOperationAsync<TProxy, T>(
 63        this TProxy proxy,
 64        string operation,
 65        PipeReader payload,
 66        PipeReader? payloadContinuation,
 67        ResponseDecodeFunc<T> responseDecodeFunc,
 68        IFeatureCollection? features = null,
 69        bool idempotent = false,
 70        CancellationToken cancellationToken = default) where TProxy : struct, IProxy
 4871    {
 4872        if (proxy.Invoker is not IInvoker invoker)
 073        {
 074            throw new InvalidOperationException("Cannot send requests using a proxy with a null invoker.");
 75        }
 76
 4877        var request = new OutgoingRequest(proxy.ServiceAddress)
 4878        {
 4879            Features = features ?? FeatureCollection.Empty,
 4880            Fields = idempotent ? _idempotentFields : ImmutableDictionary<RequestFieldKey, OutgoingFieldValue>.Empty,
 4881            Operation = operation,
 4882            Payload = payload,
 4883            PayloadContinuation = payloadContinuation
 4884        };
 85
 86        Task<IncomingResponse> responseTask;
 87        try
 4888        {
 4889            responseTask = invoker.InvokeAsync(request, cancellationToken);
 4890        }
 091        catch
 092        {
 093            request.Dispose();
 094            throw;
 95        }
 96
 97        // ReadResponseAsync is responsible for disposing the request
 4898        return ReadResponseAsync(responseTask, request);
 99
 100        async Task<T> ReadResponseAsync(Task<IncomingResponse> responseTask, OutgoingRequest request)
 48101        {
 102            try
 48103            {
 48104                IncomingResponse response = await responseTask.ConfigureAwait(false);
 48105                return await responseDecodeFunc(
 48106                    response,
 48107                    request,
 48108                    proxy,
 48109                    cancellationToken).ConfigureAwait(false);
 110            }
 111            finally
 48112            {
 48113                request.Dispose();
 48114            }
 48115        }
 48116    }
 117
 118    /// <summary>Sends a request to a service and decodes the "void" response.</summary>
 119    /// <typeparam name="TProxy">The type of the proxy struct.</typeparam>
 120    /// <param name="proxy">A proxy for the remote service.</param>
 121    /// <param name="operation">The name of the operation, as specified in Slice.</param>
 122    /// <param name="payload">The payload of the request.</param>
 123    /// <param name="payloadContinuation">The payload continuation of the request.</param>
 124    /// <param name="responseDecodeFunc">The decode function for the response payload. It decodes and throws an
 125    /// exception when the status code of the response is <see cref="StatusCode.ApplicationError" />.</param>
 126    /// <param name="features">The invocation features.</param>
 127    /// <param name="idempotent">When <see langword="true" />, the request is idempotent.</param>
 128    /// <param name="oneway">When <see langword="true" />, the request is sent one-way and an empty response is returned
 129    /// immediately after sending the request.</param>
 130    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 131    /// <returns>A task that completes when the void response is returned.</returns>
 132    /// <exception cref="SliceException">Thrown if the response carries a failure.</exception>
 133    public static Task InvokeOperationAsync<TProxy>(
 134        this TProxy proxy,
 135        string operation,
 136        PipeReader payload,
 137        PipeReader? payloadContinuation,
 138        ResponseDecodeFunc responseDecodeFunc,
 139        IFeatureCollection? features = null,
 140        bool idempotent = false,
 141        bool oneway = false,
 142        CancellationToken cancellationToken = default) where TProxy : struct, IProxy
 70143    {
 70144        if (proxy.Invoker is not IInvoker invoker)
 0145        {
 0146            throw new InvalidOperationException("Cannot send requests using a proxy with a null invoker.");
 147        }
 148
 70149        var request = new OutgoingRequest(proxy.ServiceAddress)
 70150        {
 70151            Features = features ?? FeatureCollection.Empty,
 70152            Fields = idempotent ? _idempotentFields : ImmutableDictionary<RequestFieldKey, OutgoingFieldValue>.Empty,
 70153            IsOneway = oneway,
 70154            Operation = operation,
 70155            Payload = payload,
 70156            PayloadContinuation = payloadContinuation
 70157        };
 158
 159        Task<IncomingResponse> responseTask;
 160        try
 70161        {
 70162            responseTask = invoker.InvokeAsync(request, cancellationToken);
 69163        }
 1164        catch
 1165        {
 1166            request.Dispose();
 1167            throw;
 168        }
 169
 170        // ReadResponseAsync is responsible for disposing the request
 69171        return ReadResponseAsync(responseTask, request);
 172
 173        async Task ReadResponseAsync(Task<IncomingResponse> responseTask, OutgoingRequest request)
 69174        {
 175            try
 69176            {
 69177                IncomingResponse response = await responseTask.ConfigureAwait(false);
 178
 69179                await responseDecodeFunc(
 69180                    response,
 69181                    request,
 69182                    proxy,
 69183                    cancellationToken).ConfigureAwait(false);
 40184            }
 185            finally
 69186            {
 69187                request.Dispose();
 69188            }
 40189        }
 69190    }
 191
 192    /// <summary>Converts a proxy into a proxy struct. This conversion always succeeds.</summary>
 193    /// <typeparam name="TProxy">The type of the target proxy struct.</typeparam>
 194    /// <param name="proxy">The source proxy.</param>
 195    /// <returns>A new instance of <typeparamref name="TProxy" />.</returns>
 196    public static TProxy ToProxy<TProxy>(this IProxy proxy) where TProxy : struct, IProxy =>
 3197        new() { EncodeOptions = proxy.EncodeOptions, Invoker = proxy.Invoker, ServiceAddress = proxy.ServiceAddress };
 198}