< Summary

Information
Class: IceRpc.Ice.Operations.IceProxyExtensions
Assembly: IceRpc.Ice
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Ice/Operations/IceProxyExtensions.cs
Tag: 2300_35243572715
Line coverage
86%
Covered lines: 63
Uncovered lines: 10
Coverable lines: 73
Total lines: 184
Line coverage: 86.3%
Branch coverage
75%
Covered branches: 9
Total branches: 12
Branch coverage: 75%
Method coverage
100%
Covered methods: 5
Fully covered methods: 3
Total methods: 5
Method coverage: 100%
Full method coverage: 60%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
InvokeOperationAsync(...)66.66%7666.66%
ReadResponseAsync()100%11100%
InvokeOperationAsync(...)83.33%6686.36%
ReadResponseAsync()100%11100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Ice/Operations/IceProxyExtensions.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Features;
 4using System.Collections.Immutable;
 5using System.IO.Pipelines;
 6
 7namespace IceRpc.Ice.Operations;
 8
 9/// <summary>Represents a delegate that decodes the return value from an Ice-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 return value or a <see cref="IceException" /> when the status code of the
 16/// response is <see cref="StatusCode.ApplicationError" />.</returns>
 17public delegate ValueTask<T> ResponseDecodeFunc<T>(
 18    IncomingResponse response,
 19    OutgoingRequest request,
 20    IIceProxy sender,
 21    CancellationToken cancellationToken);
 22
 23/// <summary>Represents a delegate that decodes the "void" return value from an Ice-encoded response.</summary>
 24/// <param name="response">The incoming response.</param>
 25/// <param name="request">The outgoing request.</param>
 26/// <param name="sender">The proxy that sent the request.</param>
 27/// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 28/// <returns>A value task that contains a <see cref="IceException" /> when the status code of the response is
 29/// <see cref="StatusCode.ApplicationError" />.</returns>
 30public delegate ValueTask ResponseDecodeFunc(
 31    IncomingResponse response,
 32    OutgoingRequest request,
 33    IIceProxy sender,
 34    CancellationToken cancellationToken);
 35
 36/// <summary>Provides helper methods for generated proxy structs.</summary>
 37public static class IceProxyExtensions
 38{
 239    private static readonly IDictionary<RequestFieldKey, OutgoingFieldValue> _idempotentFields =
 240        new Dictionary<RequestFieldKey, OutgoingFieldValue>
 241        {
 242            [RequestFieldKey.Idempotent] = default
 243        }.ToImmutableDictionary();
 44
 45    /// <summary>Sends a request to a service and decodes the response.</summary>
 46    /// <typeparam name="TProxy">The type of the proxy struct.</typeparam>
 47    /// <typeparam name="T">The response type.</typeparam>
 48    /// <param name="proxy">A proxy to the remote service.</param>
 49    /// <param name="operation">The name of the operation, as specified in Ice Slice.</param>
 50    /// <param name="payload">The payload of the request.</param>
 51    /// <param name="responseDecodeFunc">The decode function for the response payload. It decodes and throws an
 52    /// exception when the status code of the response is <see cref="StatusCode.ApplicationError" />.</param>
 53    /// <param name="features">The invocation features.</param>
 54    /// <param name="idempotent">When <see langword="true" />, the request is idempotent.</param>
 55    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 56    /// <returns>The operation's return value.</returns>
 57    /// <exception cref="IceException">Thrown when the response carries an Ice exception.</exception>
 58    public static Task<T> InvokeOperationAsync<TProxy, T>(
 59        this TProxy proxy,
 60        string operation,
 61        PipeReader payload,
 62        ResponseDecodeFunc<T> responseDecodeFunc,
 63        IFeatureCollection? features = null,
 64        bool idempotent = false,
 65        CancellationToken cancellationToken = default) where TProxy : struct, IIceProxy
 3866    {
 3867        if (proxy.Invoker is not IInvoker invoker)
 068        {
 069            payload.Complete();
 070            throw new InvalidOperationException("Cannot send requests using a proxy with a null invoker.");
 71        }
 72
 3873        var request = new OutgoingRequest(proxy.ServiceAddress)
 3874        {
 3875            Features = features ?? FeatureCollection.Empty,
 3876            Fields = idempotent ? _idempotentFields : ImmutableDictionary<RequestFieldKey, OutgoingFieldValue>.Empty,
 3877            Operation = operation,
 3878            Payload = payload
 3879        };
 80
 81        Task<IncomingResponse> responseTask;
 82        try
 3883        {
 3884            responseTask = invoker.InvokeAsync(request, cancellationToken);
 3885        }
 086        catch
 087        {
 088            request.Dispose();
 089            throw;
 90        }
 91
 92        // ReadResponseAsync is responsible for disposing the request
 3893        return ReadResponseAsync(responseTask, request);
 94
 95        async Task<T> ReadResponseAsync(Task<IncomingResponse> responseTask, OutgoingRequest request)
 3896        {
 97            try
 3898            {
 3899                IncomingResponse response = await responseTask.ConfigureAwait(false);
 38100                return await responseDecodeFunc(
 38101                    response,
 38102                    request,
 38103                    proxy,
 38104                    cancellationToken).ConfigureAwait(false);
 105            }
 106            finally
 38107            {
 38108                request.Dispose();
 38109            }
 38110        }
 38111    }
 112
 113    /// <summary>Sends a request to a service and decodes the "void" response.</summary>
 114    /// <typeparam name="TProxy">The type of the proxy struct.</typeparam>
 115    /// <param name="proxy">A proxy for the remote service.</param>
 116    /// <param name="operation">The name of the operation, as specified in Ice Slice.</param>
 117    /// <param name="payload">The payload of the request.</param>
 118    /// <param name="responseDecodeFunc">The decode function for the response payload. It decodes and throws an
 119    /// exception when the status code of the response is <see cref="StatusCode.ApplicationError" />.</param>
 120    /// <param name="features">The invocation features.</param>
 121    /// <param name="idempotent">When <see langword="true" />, the request is idempotent.</param>
 122    /// <param name="oneway">When <see langword="true" />, the request is sent one-way and an empty response is returned
 123    /// immediately after sending the request.</param>
 124    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 125    /// <returns>A task that completes when the void response is returned.</returns>
 126    /// <exception cref="IceException">Thrown when the response carries a failure.</exception>
 127    public static Task InvokeOperationAsync<TProxy>(
 128        this TProxy proxy,
 129        string operation,
 130        PipeReader payload,
 131        ResponseDecodeFunc responseDecodeFunc,
 132        IFeatureCollection? features = null,
 133        bool idempotent = false,
 134        bool oneway = false,
 135        CancellationToken cancellationToken = default) where TProxy : struct, IIceProxy
 75136    {
 75137        if (proxy.Invoker is not IInvoker invoker)
 0138        {
 0139            payload.Complete();
 0140            throw new InvalidOperationException("Cannot send requests using a proxy with a null invoker.");
 141        }
 142
 75143        var request = new OutgoingRequest(proxy.ServiceAddress)
 75144        {
 75145            Features = features ?? FeatureCollection.Empty,
 75146            Fields = idempotent ? _idempotentFields : ImmutableDictionary<RequestFieldKey, OutgoingFieldValue>.Empty,
 75147            IsOneway = oneway,
 75148            Operation = operation,
 75149            Payload = payload,
 75150        };
 151
 152        Task<IncomingResponse> responseTask;
 153        try
 75154        {
 75155            responseTask = invoker.InvokeAsync(request, cancellationToken);
 74156        }
 1157        catch
 1158        {
 1159            request.Dispose();
 1160            throw;
 161        }
 162
 163        // ReadResponseAsync is responsible for disposing the request
 74164        return ReadResponseAsync(responseTask, request);
 165
 166        async Task ReadResponseAsync(Task<IncomingResponse> responseTask, OutgoingRequest request)
 74167        {
 168            try
 74169            {
 74170                IncomingResponse response = await responseTask.ConfigureAwait(false);
 171
 74172                await responseDecodeFunc(
 74173                    response,
 74174                    request,
 74175                    proxy,
 74176                    cancellationToken).ConfigureAwait(false);
 45177            }
 178            finally
 74179            {
 74180                request.Dispose();
 74181            }
 45182        }
 74183    }
 184}