Skip to content

Commit

Permalink
Finish protocol method bodies (#3691)
Browse files Browse the repository at this point in the history
  • Loading branch information
m-nash committed Jun 28, 2024
1 parent 1bb4032 commit 434f058
Show file tree
Hide file tree
Showing 36 changed files with 1,210 additions and 435 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
// Licensed under the MIT License.

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Text.Json;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Generator.CSharp.Providers;
using Microsoft.Generator.CSharp.Snippets;
using static Microsoft.Generator.CSharp.Snippets.Snippet;

namespace Microsoft.Generator.CSharp.ClientModel
Expand All @@ -17,25 +17,19 @@ internal static class ScmKnownParameters
private static readonly CSharpType modelReaderWriterOptionsType = typeof(ModelReaderWriterOptions);
private static readonly CSharpType nullableModelReaderWriterOptionsType = new CSharpType(typeof(ModelReaderWriterOptions), isNullable: true);

public static readonly ParameterProvider XmlWriter = new ParameterProvider("writer", FormattableStringHelpers.Empty, typeof(XmlWriter));
public static readonly ParameterProvider NameHint = new ParameterProvider("nameHint", FormattableStringHelpers.Empty, typeof(string));
public static readonly ParameterProvider XElement = new ParameterProvider("element", FormattableStringHelpers.Empty, typeof(XElement));

public static readonly ParameterProvider Utf8JsonWriter = new ParameterProvider("writer", FormattableStringHelpers.Empty, typeof(Utf8JsonWriter));
public static readonly ParameterProvider Utf8JsonReader = new ParameterProvider("reader", FormattableStringHelpers.Empty, typeof(Utf8JsonReader), isRef: true);
public static readonly ParameterProvider JsonOptions = new ParameterProvider("options", FormattableStringHelpers.Empty, typeof(JsonSerializerOptions));
public static readonly ParameterProvider Options = new ParameterProvider("options", FormattableStringHelpers.Empty, modelReaderWriterOptionsType);
public static readonly ParameterProvider OptionalOptions = new ParameterProvider("options", FormattableStringHelpers.Empty, nullableModelReaderWriterOptionsType, DefaultOf(nullableModelReaderWriterOptionsType));
public static readonly ParameterProvider JsonElement = new ParameterProvider("element", FormattableStringHelpers.Empty, typeof(JsonElement));
public static readonly ParameterProvider Data = new ParameterProvider("data", FormattableStringHelpers.Empty, typeof(BinaryData));

private static ParameterProvider? _tokenAuth;
public static ParameterProvider TokenAuth => _tokenAuth ??= new("tokenCredential", $"The token credential to copy", ClientModelPlugin.Instance.TypeFactory.TokenCredentialType());

private static ParameterProvider? _matchConditionsParameter;
public static ParameterProvider MatchConditionsParameter => _matchConditionsParameter ??= new("matchConditions", $"The content to send as the request conditions of the request.", ClientModelPlugin.Instance.TypeFactory.MatchConditionsType(), Snippet.DefaultOf(ClientModelPlugin.Instance.TypeFactory.RequestConditionsType()));

private static ParameterProvider? _requestConditionsParameter;
public static ParameterProvider RequestConditionsParameter => _requestConditionsParameter ??= new("requestConditions", $"The content to send as the request conditions of the request.", ClientModelPlugin.Instance.TypeFactory.RequestConditionsType(), Snippet.DefaultOf(ClientModelPlugin.Instance.TypeFactory.RequestConditionsType()));
public static readonly ParameterProvider XmlWriter = new("writer", FormattableStringHelpers.Empty, typeof(XmlWriter));
public static readonly ParameterProvider NameHint = new("nameHint", FormattableStringHelpers.Empty, typeof(string));
public static readonly ParameterProvider XElement = new("element", FormattableStringHelpers.Empty, typeof(XElement));
public static readonly ParameterProvider Utf8JsonWriter = new("writer", FormattableStringHelpers.Empty, typeof(Utf8JsonWriter));
public static readonly ParameterProvider Utf8JsonReader = new("reader", FormattableStringHelpers.Empty, typeof(Utf8JsonReader), isRef: true);
public static readonly ParameterProvider JsonOptions = new("options", FormattableStringHelpers.Empty, typeof(JsonSerializerOptions));
public static readonly ParameterProvider Options = new("options", FormattableStringHelpers.Empty, modelReaderWriterOptionsType);
public static readonly ParameterProvider OptionalOptions = new("options", FormattableStringHelpers.Empty, nullableModelReaderWriterOptionsType, DefaultOf(nullableModelReaderWriterOptionsType));
public static readonly ParameterProvider JsonElement = new("element", FormattableStringHelpers.Empty, typeof(JsonElement));
public static readonly ParameterProvider Data = new("data", FormattableStringHelpers.Empty, typeof(BinaryData));
public static readonly ParameterProvider TokenAuth = new("tokenCredential", $"The token credential to copy", ClientModelPlugin.Instance.TypeFactory.TokenCredentialType());
public static readonly ParameterProvider MatchConditionsParameter = new("matchConditions", $"The content to send as the request conditions of the request.", ClientModelPlugin.Instance.TypeFactory.MatchConditionsType(), DefaultOf(ClientModelPlugin.Instance.TypeFactory.MatchConditionsType()));
public static readonly ParameterProvider RequestOptions = new("options", $"The request options, which can override default behaviors of the client pipeline on a per-call basis.", typeof(RequestOptions));
public static readonly ParameterProvider BinaryContent = new("content", $"The content to send as the body of the request.", typeof(BinaryContent));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ protected override TypeProvider[] BuildTypeProviders()
..baseTypes,
..BuildClients(),
new ModelSerializationExtensionsProvider(),
new TypeFormattersProvider()
new TypeFormattersProvider(),
new ClientPipelineExtensionsProvider(),
new ErrorResultProvider()
];
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using Microsoft.Generator.CSharp.ClientModel.Providers;
using Microsoft.Generator.CSharp.Input;
Expand All @@ -21,28 +22,8 @@ public class ScmTypeFactory : TypeFactory
/// <param name="enclosingType">The enclosing type of the operation.</param>
public override MethodProviderCollection CreateMethodProviders(InputOperation operation, TypeProvider enclosingType) => new ScmMethodProviderCollection(operation, enclosingType);

public virtual CSharpType MatchConditionsType()
{
// TO-DO: Determine what the correct type is for MatchConditions: https://github.com/Azure/autorest.csharp/issues/4166
throw new NotImplementedException();
}
public virtual CSharpType MatchConditionsType() => typeof(PipelineMessageClassifier);

public virtual CSharpType RequestConditionsType()
{
// TO-DO: Determine what the correct type is for RequestConditions: https://github.com/Azure/autorest.csharp/issues/4166
throw new NotImplementedException();
}

public virtual CSharpType TokenCredentialType()
{
// TO-DO: Determine what the correct type is for TokenCredential: https://github.com/Azure/autorest.csharp/issues/4166
throw new NotImplementedException();
}

public virtual CSharpType PageResponseType()
{
// TO-DO: Determine what the correct type is for Page: https://github.com/Azure/autorest.csharp/issues/4166
throw new NotImplementedException();
}
public virtual CSharpType TokenCredentialType() => typeof(ApiKeyCredential);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Generator.CSharp.ClientModel.Snippets;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Providers;
using Microsoft.Generator.CSharp.Statements;
using static Microsoft.Generator.CSharp.Snippets.Snippet;

namespace Microsoft.Generator.CSharp.ClientModel.Providers
{
internal class ClientPipelineExtensionsProvider : TypeProvider
{
public override string RelativeFilePath => Path.Combine("src", "Generated", "Internal", $"{Name}.cs");
public override string Name { get; }
private static readonly Lazy<ClientPipelineExtensionsProvider> _instance = new(() => new ClientPipelineExtensionsProvider());

public static ClientPipelineExtensionsProvider Instance => _instance.Value;

private const string _processMessageAsync = "ProcessMessageAsync";
private const string _processMessage = "ProcessMessage";
Expand All @@ -22,31 +27,155 @@ internal class ClientPipelineExtensionsProvider : TypeProvider

private ParameterProvider _pipelineParam;
private ParameterProvider _messageParam;
private ParameterProvider _requestContextParam;
private MemberExpression _messageResponse;
private ParameterProvider _requestOptionsParam;
private ClientPipelineSnippet _pipeline;
private PipelineMessageSnippet _message;
private RequestOptionsSnippet _options;

internal ClientPipelineExtensionsProvider()
public ClientPipelineExtensionsProvider()
{
Name = "ClientPipelineExtensions";
_pipelineParam = new ParameterProvider("pipeline", $"The pipeline.", typeof(ClientPipeline));
_messageParam = new ParameterProvider("message", $"The message.", typeof(PipelineMessage));
_requestContextParam = new ParameterProvider("requestContext", $"The request context.", typeof(RequestOptions));
_messageResponse = new MemberExpression(_messageParam, "Response");
_pipelineParam = new ParameterProvider("pipeline", FormattableStringHelpers.Empty, typeof(ClientPipeline));
_messageParam = new ParameterProvider("message", FormattableStringHelpers.Empty, typeof(PipelineMessage));
_requestOptionsParam = new ParameterProvider("options", FormattableStringHelpers.Empty, typeof(RequestOptions));
_pipeline = new ClientPipelineSnippet(_pipelineParam);
_message = new PipelineMessageSnippet(_messageParam);
_options = new RequestOptionsSnippet(_requestOptionsParam);
}

protected override TypeSignatureModifiers GetDeclarationModifiers()
{
return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static;
}

internal PipelineResponseSnippet ProcessMessage(IReadOnlyList<ValueExpression> arguments, bool isAsync)
public override string Name => "ClientPipelineExtensions";

public override string RelativeFilePath => Path.Combine("src", "Generated", "Internal", $"{Name}.cs");

protected override MethodProvider[] BuildMethods()
{
return new(new InvokeStaticMethodExpression(Type, isAsync ? _processMessageAsync : _processMessage, arguments, CallAsExtension: true, CallAsAsync: isAsync));
return
[
BuildProcessMessageAsync(),
BuildProcessMessage(),
ProcessHeadAsBoolMessageAsync(),
ProcessHeadAsBoolMessage()
];
}

internal ClientResultSnippet ProcessHeadAsBoolMessage(IReadOnlyList<ValueExpression> arguments, bool isAsync)
private MethodProvider ProcessHeadAsBoolMessage()
{
return new(new InvokeStaticMethodExpression(Type, isAsync ? _processHeadAsBoolMessageAsync : _processHeadAsBoolMessage, arguments, CallAsExtension: true, CallAsAsync: isAsync));
MethodSignature signature = GetProcessHeadAsBoolMessageSignature(false);
var responseVariable = new VariableExpression(typeof(PipelineResponse), "response");
var response = new PipelineResponseSnippet(responseVariable);
return new MethodProvider(signature, new MethodBodyStatement[]
{
new DeclarationExpression(responseVariable, false).Assign(_pipeline.ProcessMessage(_message, _options, false)).Terminate(),
GetProcessHeadAsBoolMessageBody(response)
}, this);
}

private MethodProvider ProcessHeadAsBoolMessageAsync()
{
MethodSignature signature = GetProcessHeadAsBoolMessageSignature(true);
var responseVariable = new VariableExpression(typeof(PipelineResponse), "response");
var response = new PipelineResponseSnippet(responseVariable);
return new MethodProvider(signature, new MethodBodyStatement[]
{
new DeclarationExpression(responseVariable, false).Assign(_pipeline.ProcessMessage(_message, _options, true)).Terminate(),
GetProcessHeadAsBoolMessageBody(response)
}, this);
}

private MethodBodyStatement GetProcessHeadAsBoolMessageBody(PipelineResponseSnippet response)
{
return new MethodBodyStatement[]
{
new SwitchStatement(new MemberExpression(response, "Status"), new SwitchCaseStatement[]
{
new SwitchCaseStatement(ValueExpression.Empty.GreaterThanOrEqual(Literal(200)).AndExpr(ValueExpression.Empty.LessThan(Literal(300))), new MethodBodyStatement[]
{
Return(ClientResultSnippet.FromValue(typeof(bool), True, response))
}),
new SwitchCaseStatement(ValueExpression.Empty.GreaterThanOrEqual(Literal(400)).AndExpr(ValueExpression.Empty.LessThan(Literal(500))), new MethodBodyStatement[]
{
Return(ClientResultSnippet.FromValue(typeof(bool), False, response))
}),
new SwitchCaseStatement(Array.Empty<ValueExpression>(), new MethodBodyStatement[]
{
Return(new NewInstanceExpression(ErrorResultSnippet.ErrorResultType.MakeGenericType([typeof(bool)]), [response, new NewInstanceExpression(typeof(ClientResultException), [response])]))
})
}),
};
}

private MethodSignature GetProcessHeadAsBoolMessageSignature(bool isAsync)
{
var modifiers = MethodSignatureModifiers.Public | MethodSignatureModifiers.Static | MethodSignatureModifiers.Extension;
if (isAsync)
{
modifiers |= MethodSignatureModifiers.Async;
}
return new MethodSignature(
isAsync ? "ProcessHeadAsBoolMessageAsync" : "ProcessHeadAsBoolMessage",
null,
modifiers,
isAsync ? typeof(ValueTask<ClientResult<bool>>) : typeof(ClientResult<bool>),
null,
[_pipelineParam, _messageParam, _requestOptionsParam]);
}

private MethodProvider BuildProcessMessage()
{
MethodSignature signature = GetProcessMessageSignature(false);

var clientErrorNoThrow = FrameworkEnumValue(ClientErrorBehaviors.NoThrow);
return new MethodProvider(signature, new MethodBodyStatement[]
{
_pipeline.Invoke(nameof(ClientPipeline.Send), [_message]).Terminate(),
MethodBodyStatement.EmptyLine,
new IfStatement(_message.Response.IsError.And(new BinaryOperatorExpression("&", _options.Property("ErrorOptions", true), clientErrorNoThrow).NotEqual(clientErrorNoThrow)))
{
Throw(New.Instance(typeof(ClientResultException), _message.Response))
},
MethodBodyStatement.EmptyLine,
Declare(typeof(PipelineResponse), "response", new TernaryConditionalExpression(_message.BufferResponse, _message.Response, _message.ExtractResponse()), out var response),
Return(response)
}, this);
}

private MethodSignature GetProcessMessageSignature(bool isAsync)
{
var modifiers = MethodSignatureModifiers.Public | MethodSignatureModifiers.Static | MethodSignatureModifiers.Extension;
if (isAsync)
{
modifiers |= MethodSignatureModifiers.Async;
}
return new MethodSignature(
isAsync ? "ProcessMessageAsync" : "ProcessMessage",
null,
modifiers,
isAsync ? typeof(ValueTask<PipelineResponse>) : typeof(PipelineResponse),
null,
[_pipelineParam, _messageParam, _requestOptionsParam]);
}

private MethodProvider BuildProcessMessageAsync()
{
MethodSignature signature = GetProcessMessageSignature(true);

var clientErrorNoThrow = FrameworkEnumValue(ClientErrorBehaviors.NoThrow);
return new MethodProvider(signature, new MethodBodyStatement[]
{
_pipeline.Expression.Invoke(nameof(ClientPipeline.SendAsync), [_message], true).Terminate(),
MethodBodyStatement.EmptyLine,
new IfStatement(_message.Response.IsError.And(new BinaryOperatorExpression("&", _options.Property("ErrorOptions", true), clientErrorNoThrow).NotEqual(clientErrorNoThrow)))
{
Throw(new InvokeStaticMethodExpression(typeof(ClientResultException), nameof(ClientResultException.CreateAsync), [_message.Response], CallAsAsync: true))
},
MethodBodyStatement.EmptyLine,
Declare(typeof(PipelineResponse), "response", new TernaryConditionalExpression(_message.BufferResponse, _message.Response, _message.ExtractResponse()), out var response),
Return(response)
}, this);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.IO;
using Microsoft.Generator.CSharp.ClientModel.Snippets;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Providers;
using Microsoft.Generator.CSharp.Snippets;
using Microsoft.Generator.CSharp.Statements;

namespace Microsoft.Generator.CSharp.ClientModel.Providers
{
public sealed class ClientProvider : TypeProvider
public class ClientProvider : TypeProvider
{
private readonly InputClient _inputClient;

Expand All @@ -20,6 +24,25 @@ public ClientProvider(InputClient inputClient)
{
_inputClient = inputClient;
Name = inputClient.Name.ToCleanName();
PipelineField = new FieldProvider(FieldModifiers.Private, typeof(ClientPipeline), "_pipeline");
}

public FieldProvider PipelineField { get; }

protected override FieldProvider[] BuildFields()
{
return [PipelineField];
}

protected override MethodProvider[] BuildConstructors()
{
return
[
new MethodProvider(
new ConstructorSignature(Type, $"{_inputClient.Description}", MethodSignatureModifiers.Public, []),
new MethodBodyStatement[] { PipelineField.Assign(ClientPipelineSnippet.Create()).Terminate() },
this)
];
}

protected override MethodProvider[] BuildMethods()
Expand Down
Loading

0 comments on commit 434f058

Please sign in to comment.