-
Notifications
You must be signed in to change notification settings - Fork 240
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ClientUriBuilder helper class implementation (#3704)
Fixes: #3680 --------- Co-authored-by: ShivangiReja <[email protected]>
- Loading branch information
1 parent
a7405e6
commit eaca7a7
Showing
4 changed files
with
933 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
336 changes: 336 additions & 0 deletions
336
...enerator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientUriBuilderProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,336 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text; | ||
using Microsoft.Generator.CSharp.Expressions; | ||
using Microsoft.Generator.CSharp.Providers; | ||
using Microsoft.Generator.CSharp.Statements; | ||
using Microsoft.Generator.CSharp.Snippets; | ||
using static Microsoft.Generator.CSharp.Snippets.Snippet; | ||
using static Microsoft.Generator.CSharp.ClientModel.Snippets.TypeFormattersSnippet; | ||
using Microsoft.Generator.CSharp.Primitives; | ||
|
||
namespace Microsoft.Generator.CSharp.ClientModel.Providers | ||
{ | ||
internal sealed class ClientUriBuilderProvider : TypeProvider | ||
{ | ||
protected override TypeSignatureModifiers GetDeclarationModifiers() | ||
{ | ||
return TypeSignatureModifiers.Internal; | ||
} | ||
|
||
public override string RelativeFilePath => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); | ||
|
||
public override string Name => "ClientUriBuilder"; | ||
|
||
private readonly FieldProvider _uriBuilderField = new(FieldModifiers.Private, typeof(UriBuilder), "_uriBuilder"); | ||
private readonly FieldProvider _pathBuilderField = new(FieldModifiers.Private, typeof(StringBuilder), "_pathBuilder"); | ||
private readonly FieldProvider _queryBuilderField = new(FieldModifiers.Private, typeof(StringBuilder), "_queryBuilder"); | ||
|
||
protected override FieldProvider[] BuildFields() | ||
{ | ||
return [_uriBuilderField, _pathBuilderField, _queryBuilderField]; | ||
} | ||
|
||
private PropertyProvider? _uriBuilderProperty; | ||
private PropertyProvider UriBuilderProperty => _uriBuilderProperty ??= new( | ||
modifiers: MethodSignatureModifiers.Private, | ||
name: "UriBuilder", | ||
type: typeof(UriBuilder), | ||
body: new ExpressionPropertyBody(new BinaryOperatorExpression(" ??= ", _uriBuilderField, New.Instance(typeof(UriBuilder)))), | ||
description: null); | ||
|
||
internal ValueExpression UriBuilderPath => new MemberExpression(UriBuilderProperty, "Path"); | ||
internal ValueExpression UriBuilderQuery => new MemberExpression(UriBuilderProperty, "Query"); | ||
|
||
private PropertyProvider? _pathBuilderProperty; | ||
private PropertyProvider PathBuilderProperty => _pathBuilderProperty ??= new( | ||
modifiers: MethodSignatureModifiers.Private, | ||
name: "PathBuilder", | ||
type: typeof(StringBuilder), | ||
body: new ExpressionPropertyBody(new BinaryOperatorExpression(" ??= ", _pathBuilderField, New.Instance(typeof(StringBuilder), UriBuilderPath))), | ||
description: null); | ||
|
||
private PropertyProvider? _queryBuilderProperty; | ||
private PropertyProvider QueryBuilderProperty => _queryBuilderProperty ??= new( | ||
modifiers: MethodSignatureModifiers.Private, | ||
name: "QueryBuilder", | ||
type: typeof(StringBuilder), | ||
body: new ExpressionPropertyBody(new BinaryOperatorExpression(" ??= ", _queryBuilderField, New.Instance(typeof(StringBuilder), UriBuilderQuery))), | ||
description: null); | ||
|
||
protected override PropertyProvider[] BuildProperties() | ||
{ | ||
return [UriBuilderProperty, PathBuilderProperty, QueryBuilderProperty]; | ||
} | ||
|
||
protected override MethodProvider[] BuildConstructors() | ||
{ | ||
var signature = new ConstructorSignature( | ||
Type: Type, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: Array.Empty<ParameterProvider>(), | ||
Description: null); | ||
return [new MethodProvider(signature, MethodBodyStatement.Empty, this)]; | ||
} | ||
|
||
protected override MethodProvider[] BuildMethods() | ||
{ | ||
var methods = new List<MethodProvider>(); | ||
|
||
methods.Add(BuildResetMethod()); | ||
|
||
methods.AddRange(BuildAppendPathMethods()); | ||
methods.AddRange(BuildAppendQueryMethods()); | ||
methods.AddRange(BuildAppendQueryDelimitedMethods()); | ||
|
||
methods.Add(BuildToUriMethod()); | ||
|
||
return methods.ToArray(); | ||
} | ||
|
||
private const string _resetMethodName = "Reset"; | ||
private MethodProvider BuildResetMethod() | ||
{ | ||
var uriParameter = new ParameterProvider("uri", $"The uri.", typeof(Uri)); | ||
var signature = new MethodSignature( | ||
Name: _resetMethodName, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: new[] | ||
{ | ||
uriParameter | ||
}, | ||
ReturnType: null, | ||
Description: null, ReturnDescription: null); | ||
|
||
var body = new MethodBodyStatement[] | ||
{ | ||
_uriBuilderField.Assign(New.Instance(_uriBuilderField.Type, uriParameter)).Terminate(), | ||
_pathBuilderField.Assign(New.Instance(_pathBuilderField.Type, UriBuilderPath)).Terminate(), | ||
_queryBuilderField.Assign(New.Instance(_queryBuilderField.Type, UriBuilderQuery)).Terminate() | ||
}; | ||
|
||
return new(signature, body, this); | ||
} | ||
|
||
private const string _appendPathMethodName = "AppendPath"; | ||
private MethodProvider[] BuildAppendPathMethods() | ||
{ | ||
var valueParameter = new ParameterProvider("value", $"The value.", typeof(string)); | ||
var escapeParameter = new ParameterProvider("escape", $"The escape", typeof(bool)); | ||
var signature = new MethodSignature( | ||
Name: _appendPathMethodName, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: [valueParameter, escapeParameter], | ||
ReturnType: null, | ||
Description: null, ReturnDescription: null); | ||
|
||
var value = new StringSnippet(valueParameter); | ||
var escape = new BoolSnippet(escapeParameter); | ||
var pathBuilder = new StringBuilderSnippet(PathBuilderProperty); | ||
MethodBodyStatement body = new MethodBodyStatement[] | ||
{ | ||
MethodBodyStatement.Empty, | ||
new IfStatement(escape) | ||
{ | ||
value.Assign(new InvokeStaticMethodExpression(typeof(Uri), nameof(Uri.EscapeDataString), new[]{ value.Expression })).Terminate() | ||
}, | ||
MethodBodyStatement.Empty, | ||
new IfStatement(pathBuilder.Length.Expression.GreaterThan(Int(0)).And(new IndexerExpression(pathBuilder, pathBuilder.Length - Int(1)).Equal(Literal('/'))).And(new IndexerExpression(value, Int(0)).Equal(Literal('/')))) | ||
{ | ||
pathBuilder.Remove(pathBuilder.Length - Int(1), Int(1)).Terminate() | ||
}, | ||
MethodBodyStatement.Empty, | ||
pathBuilder.Append(value).Terminate(), | ||
UriBuilderPath.Assign(pathBuilder.Expression.InvokeToString()).Terminate() | ||
}; | ||
|
||
return | ||
[ | ||
new(signature, body, this), | ||
BuildAppendPathMethod(typeof(bool), false, false), | ||
BuildAppendPathMethod(typeof(float), true, false), | ||
BuildAppendPathMethod(typeof(double), true, false), | ||
BuildAppendPathMethod(typeof(int), true, false), | ||
BuildAppendPathMethod(typeof(byte[]), true, true), | ||
BuildAppendPathMethod(typeof(IEnumerable<string>), true, false), | ||
BuildAppendPathMethod(typeof(DateTimeOffset), true, true), | ||
BuildAppendPathMethod(typeof(TimeSpan), true, true), | ||
BuildAppendPathMethod(typeof(Guid), true, false), | ||
BuildAppendPathMethod(typeof(long), true, false) | ||
]; | ||
} | ||
|
||
private MethodProvider BuildAppendPathMethod(CSharpType valueType, bool escapeDefaultValue, bool hasFormat) | ||
{ | ||
var valueParameter = new ParameterProvider("value", $"The value.", valueType); | ||
var escapeParameter = new ParameterProvider("escape", $"The escape.", typeof(bool), Bool(escapeDefaultValue)); | ||
var formatParameter = new ParameterProvider("format", $"The format", typeof(string)); | ||
var parameters = hasFormat | ||
? new[] { valueParameter, formatParameter, escapeParameter } | ||
: new[] { valueParameter, escapeParameter }; | ||
|
||
var signature = new MethodSignature( | ||
Name: _appendPathMethodName, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: parameters, | ||
ReturnType: null, | ||
Description: null, ReturnDescription: null); | ||
var convertToStringExpression = ConvertToString(valueParameter, hasFormat ? (ValueExpression)formatParameter : null); | ||
var body = new InvokeInstanceMethodExpression(null, _appendPathMethodName, new[] { convertToStringExpression.Expression, escapeParameter }, null, false); | ||
|
||
return new(signature, body, this); | ||
} | ||
|
||
private const string _appendQueryMethodName = "AppendQuery"; | ||
private MethodProvider[] BuildAppendQueryMethods() | ||
{ | ||
var nameParameter = new ParameterProvider("name", $"The name.", typeof(string)); | ||
var valueParameter = new ParameterProvider("value", $"The value.", typeof(string)); | ||
var escapeParameter = new ParameterProvider("escape", $"The escape.", typeof(bool)); | ||
|
||
var signature = new MethodSignature( | ||
Name: _appendQueryMethodName, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: new[] { nameParameter, valueParameter, escapeParameter }, | ||
ReturnType: null, | ||
Description: null, ReturnDescription: null); | ||
|
||
var name = new StringSnippet(nameParameter); | ||
var value = new StringSnippet(valueParameter); | ||
var escape = new BoolSnippet(escapeParameter); | ||
var queryBuilder = new StringBuilderSnippet(QueryBuilderProperty); | ||
var body = new MethodBodyStatement[] | ||
{ | ||
MethodBodyStatement.Empty, | ||
new IfStatement(queryBuilder.Length.Expression.GreaterThan(Int(0))) | ||
{ | ||
queryBuilder.Append(Literal('&')).Terminate() | ||
}, | ||
MethodBodyStatement.Empty, | ||
new IfStatement(escape) | ||
{ | ||
value.Assign(new InvokeStaticMethodExpression(typeof(Uri), nameof(Uri.EscapeDataString), new[] { value.Expression })).Terminate() | ||
}, | ||
MethodBodyStatement.Empty, | ||
queryBuilder.Append(name).Terminate(), | ||
queryBuilder.Append(Literal('=')).Terminate(), | ||
queryBuilder.Append(value).Terminate() | ||
}; | ||
|
||
return | ||
[ | ||
new MethodProvider(signature, body, this), | ||
BuildAppendQueryMethod(typeof(bool), false, false), | ||
BuildAppendQueryMethod(typeof(float), true, false), | ||
BuildAppendQueryMethod(typeof(DateTimeOffset), true, true), | ||
BuildAppendQueryMethod(typeof(TimeSpan), true, true), | ||
BuildAppendQueryMethod(typeof(double), true, false), | ||
BuildAppendQueryMethod(typeof(decimal), true, false), | ||
BuildAppendQueryMethod(typeof(int), true, false), | ||
BuildAppendQueryMethod(typeof(long), true, false), | ||
BuildAppendQueryMethod(typeof(TimeSpan), true, false), | ||
BuildAppendQueryMethod(typeof(byte[]), true, true), | ||
BuildAppendQueryMethod(typeof(Guid), true, false) | ||
]; | ||
} | ||
|
||
private MethodProvider BuildAppendQueryMethod(CSharpType valueType, bool escapeDefaultValue, bool hasFormat) | ||
{ | ||
var nameParameter = new ParameterProvider("name", $"The name.", typeof(string)); | ||
var valueParameter = new ParameterProvider("value", $"The value.", valueType); | ||
var escapeParameter = new ParameterProvider("escape", $"The escape.", typeof(bool), Bool(escapeDefaultValue)); | ||
var formatParameter = new ParameterProvider("format", $"The format.", typeof(string)); | ||
var parameters = hasFormat | ||
? new[] { nameParameter, valueParameter, formatParameter, escapeParameter } | ||
: new[] { nameParameter, valueParameter, escapeParameter }; | ||
|
||
var signature = new MethodSignature( | ||
Name: _appendQueryMethodName, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: parameters, | ||
ReturnType: null, | ||
Description: null, ReturnDescription: null); | ||
var convertToStringExpression = ConvertToString(valueParameter, hasFormat ? (ValueExpression)formatParameter : null); | ||
var body = new InvokeInstanceMethodExpression(null, _appendQueryMethodName, new[] { nameParameter, convertToStringExpression.Expression, escapeParameter }, null, false); | ||
|
||
return new(signature, body, this); | ||
} | ||
|
||
private MethodProvider[] BuildAppendQueryDelimitedMethods() | ||
{ | ||
return [ BuildAppendQueryDelimitedMethod(false), BuildAppendQueryDelimitedMethod(true)]; | ||
} | ||
|
||
private readonly CSharpType _t = typeof(IEnumerable<>).GetGenericArguments()[0]; | ||
|
||
private const string _appendQueryDelimitedMethodName = "AppendQueryDelimited"; | ||
private MethodProvider BuildAppendQueryDelimitedMethod(bool hasFormat) | ||
{ | ||
var nameParameter = new ParameterProvider("name", $"The name.", typeof(string)); | ||
var valueParameter = new ParameterProvider("value", $"The value.", new CSharpType(typeof(IEnumerable<>), _t)); | ||
var delimiterParameter = new ParameterProvider("delimiter", $"The delimiter.", typeof(string)); | ||
var formatParameter = new ParameterProvider("format", $"The format.", typeof(string)); | ||
var escapeParameter = new ParameterProvider("escape", $"The escape.", typeof(bool), Bool(true)); | ||
|
||
var parameters = hasFormat | ||
? new[] { nameParameter, valueParameter, delimiterParameter, formatParameter, escapeParameter } | ||
: new[] { nameParameter, valueParameter, delimiterParameter, escapeParameter }; | ||
var signature = new MethodSignature( | ||
Name: _appendQueryDelimitedMethodName, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: parameters, | ||
ReturnType: null, | ||
GenericArguments: new[] { _t }, | ||
Description: null, ReturnDescription: null); | ||
|
||
var name = new StringSnippet(nameParameter); | ||
var value = new EnumerableSnippet(_t, valueParameter); | ||
var delimiter = new StringSnippet(delimiterParameter); | ||
var escape = new BoolSnippet(escapeParameter); | ||
|
||
var v = new VariableExpression(_t, "v"); | ||
var convertToStringExpression = ConvertToString(v, hasFormat ? new StringSnippet(formatParameter).Expression : null); | ||
var body = new[] | ||
{ | ||
Declare("stringValues", value.Select(new StringSnippet(new FuncExpression(new[] {v.Declaration}, convertToStringExpression))), out var stringValues), | ||
new InvokeInstanceMethodExpression(null, _appendQueryMethodName, new[] { name.Expression, StringSnippet.Join(delimiter, stringValues), escape }, null, false).Terminate() | ||
}; | ||
|
||
return new(signature, body, this); | ||
} | ||
|
||
private const string _toUriMethodName = "ToUri"; | ||
private MethodProvider BuildToUriMethod() | ||
{ | ||
var signature = new MethodSignature( | ||
Name: _toUriMethodName, | ||
Modifiers: MethodSignatureModifiers.Public, | ||
Parameters: Array.Empty<ParameterProvider>(), | ||
ReturnType: typeof(Uri), | ||
Description: null, ReturnDescription: null); | ||
|
||
var pathBuilder = (ValueExpression)_pathBuilderField; | ||
var queryBuilder = (ValueExpression)_queryBuilderField; | ||
var body = new MethodBodyStatement[] | ||
{ | ||
new IfStatement(pathBuilder.NotEqual(Null)) | ||
{ | ||
UriBuilderPath.Assign(pathBuilder.InvokeToString()).Terminate() | ||
}, | ||
MethodBodyStatement.Empty, | ||
new IfStatement(queryBuilder.NotEqual(Null)) | ||
{ | ||
UriBuilderQuery.Assign(queryBuilder.InvokeToString()).Terminate() | ||
}, | ||
MethodBodyStatement.Empty, | ||
Return(new MemberExpression(UriBuilderProperty, nameof(UriBuilder.Uri))) | ||
}; | ||
|
||
return new(signature, body, this); | ||
} | ||
} | ||
} |
Oops, something went wrong.