Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Parameter & PropertyDeclaration Classes Instead of Records #3450

Merged
merged 10 commits into from
May 30, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ internal CSharpMethod BuildJsonModelWriteMethod()
/// </summary>
internal CSharpMethod BuildJsonModelCreateMethod()
{
Parameter utf8JsonReaderParameter = new("reader", null, typeof(Utf8JsonReader), null, ValidationType.None, null, IsRef: true);
Parameter utf8JsonReaderParameter = new("reader", null, typeof(Utf8JsonReader), null, ValidationType.None, null, isRef: true);
// T IJsonModel<T>.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options)
var typeOfT = GetModelArgumentType(_iJsonModelTInterface);
return new CSharpMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ internal class ScmTypeFactory : TypeFactory

public override Parameter CreateCSharpParam(InputParameter inputParameter)
{
return Parameter.FromInputParameter(inputParameter);
return new Parameter(inputParameter);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;

namespace Microsoft.Generator.CSharp
{
internal record IndexerDeclaration(FormattableString? Description, MethodSignatureModifiers Modifiers, CSharpType PropertyType, Parameter IndexerParameter, PropertyBody PropertyBody, IReadOnlyDictionary<CSharpType, FormattableString>? Exceptions = null, CSharpType? ExplicitInterface = null)
: PropertyDeclaration(Description, Modifiers, PropertyType, "this", PropertyBody, Exceptions, ExplicitInterface); // the name of an indexer is always "this"
internal class IndexerDeclaration : PropertyDeclaration
{
public Parameter IndexerParameter { get; }
public IndexerDeclaration(FormattableString? description, MethodSignatureModifiers modifiers, CSharpType propertyType, Parameter indexerParameter, PropertyBody propertyBody, IReadOnlyDictionary<CSharpType, FormattableString>? exceptions = null, CSharpType? explicitInterface = null)
: base(description, modifiers, propertyType, "this", propertyBody, exceptions, explicitInterface)
jorgerangel-msft marked this conversation as resolved.
Show resolved Hide resolved
{
IndexerParameter = indexerParameter;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,17 @@ public KnownParameters(TypeFactory typeFactory)
private static readonly CSharpType ResponseType = new(CodeModelPlugin.Instance.Configuration.ApiTypes.ResponseType);

public Parameter TokenAuth => new("tokenCredential", $"The token credential to copy", TypeFactory.TokenCredentialType(), null, ValidationType.None, null);
public Parameter PageSizeHint => new("pageSizeHint", $"The number of items per {TypeFactory.PageResponseType():C} that should be requested (from service operations that support it). It's not guaranteed that the value will be respected.", new CSharpType(typeof(int), true), null, ValidationType.None, null);
public Parameter MatchConditionsParameter => new("matchConditions", $"The content to send as the request conditions of the request.", TypeFactory.MatchConditionsType(), Snippets.DefaultOf(TypeFactory.RequestConditionsType()), ValidationType.None, null, RequestLocation: RequestLocation.Header);
public Parameter RequestConditionsParameter => new("requestConditions", $"The content to send as the request conditions of the request.", TypeFactory.RequestConditionsType(), Snippets.DefaultOf(TypeFactory.RequestConditionsType()), ValidationType.None, null, RequestLocation: RequestLocation.Header);
public Parameter MatchConditionsParameter => new("matchConditions", $"The content to send as the request conditions of the request.", TypeFactory.MatchConditionsType(), Snippets.DefaultOf(TypeFactory.RequestConditionsType()), ValidationType.None, null, requestLocation: RequestLocation.Header);
public Parameter RequestConditionsParameter => new("requestConditions", $"The content to send as the request conditions of the request.", TypeFactory.RequestConditionsType(), Snippets.DefaultOf(TypeFactory.RequestConditionsType()), ValidationType.None, null, requestLocation: RequestLocation.Header);

public static readonly Parameter Pipeline = new("pipeline", $"The HTTP pipeline for sending and receiving REST requests and responses", new CSharpType(CodeModelPlugin.Instance.Configuration.ApiTypes.HttpPipelineType), null, ValidationType.AssertNotNull, null);
public static readonly Parameter KeyAuth = new("keyCredential", $"The key credential to copy", new CSharpType(CodeModelPlugin.Instance.Configuration.ApiTypes.KeyCredentialType), null, ValidationType.None, null);
public static readonly Parameter Endpoint = new("endpoint", $"Service endpoint", new CSharpType(typeof(Uri)), null, ValidationType.None, null, RequestLocation: RequestLocation.Uri, IsEndpoint: true);
public static readonly Parameter Endpoint = new("endpoint", $"Service endpoint", new CSharpType(typeof(Uri)), null, ValidationType.None, null, requestLocation: RequestLocation.Uri, isEndpoint: true);

public static readonly Parameter NextLink = new("nextLink", $"Continuation token", typeof(string), null, ValidationType.None, null);

public static readonly Parameter RequestContent = new("content", $"The content to send as the body of the request.", RequestContentType, null, ValidationType.AssertNotNull, null, RequestLocation: RequestLocation.Body);
public static readonly Parameter RequestContentNullable = new("content", $"The content to send as the body of the request.", RequestContentNullableType, null, ValidationType.None, null, RequestLocation: RequestLocation.Body);
public static readonly Parameter RequestContent = new("content", $"The content to send as the body of the request.", RequestContentType, null, ValidationType.AssertNotNull, null, requestLocation: RequestLocation.Body);
public static readonly Parameter RequestContentNullable = new("content", $"The content to send as the body of the request.", RequestContentNullableType, null, ValidationType.None, null, requestLocation: RequestLocation.Body);

public static readonly Parameter RequestContext = new("context", $"The request context, which can override default behaviors of the client pipeline on a per-call basis.", RequestContextNullableType, Snippets.DefaultOf(RequestContextNullableType), ValidationType.None, null);
public static readonly Parameter RequestContextRequired = new("context", $"The request context, which can override default behaviors of the client pipeline on a per-call basis.", RequestContextType, null, ValidationType.None, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,6 @@ public sealed record MethodSignature(string Name, FormattableString? Summary, Fo
{
public static IEqualityComparer<MethodSignature> ParameterAndReturnTypeEqualityComparer = new MethodSignatureParameterAndReturnTypeEqualityComparer();

/// <summary>
/// Returns a new instance of MethodSignature with all required parameters.
/// </summary>
public MethodSignature WithParametersRequired()
jorgerangel-msft marked this conversation as resolved.
Show resolved Hide resolved
{
if (Parameters.All(p => p.DefaultValue is null))
{
return this;
}
return this with { Parameters = Parameters.Select(p => p.ToRequired()).ToList() };
}

/// <summary>
/// Gets the C# reference string for the method.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,85 +50,12 @@ protected override PropertyDeclaration[] BuildProperties()
for (int i = 0; i < propertiesCount; i++)
{
var property = _inputModel.Properties[i];
propertyDeclarations[i] = BuildPropertyDeclaration(property);
propertyDeclarations[i] = new PropertyDeclaration(property);
}

return propertyDeclarations;
}

private PropertyDeclaration BuildPropertyDeclaration(InputModelProperty property)
{
var propertyType = CodeModelPlugin.Instance.TypeFactory.CreateCSharpType(property.Type);
var serializationFormat = CodeModelPlugin.Instance.TypeFactory.GetSerializationFormat(property.Type);
var propHasSetter = PropertyHasSetter(propertyType, property);
MethodSignatureModifiers setterModifier = propHasSetter ? MethodSignatureModifiers.Public : MethodSignatureModifiers.None;

var propertyDeclaration = new PropertyDeclaration(
Description: PropertyDescriptionBuilder.BuildPropertyDescription(property, propertyType, serializationFormat, !propHasSetter),
Modifiers: MethodSignatureModifiers.Public,
Type: propertyType,
Name: property.Name.FirstCharToUpperCase(),
Body: new AutoPropertyBody(propHasSetter, setterModifier, GetPropertyInitializationValue(property, propertyType))
);

return propertyDeclaration;
}

/// <summary>
/// Returns true if the property has a setter.
/// </summary>
/// <param name="type">The <see cref="CSharpType"/> of the property.</param>
/// <param name="prop">The <see cref="InputModelProperty"/>.</param>
private bool PropertyHasSetter(CSharpType type, InputModelProperty prop)
{
if (prop.IsDiscriminator)
{
return true;
}

if (prop.IsReadOnly)
{
return false;
}

if (IsStruct)
{
return false;
}

if (type.IsLiteral && prop.IsRequired)
{
return false;
}

if (type.IsCollection && !type.IsReadOnlyMemory)
{
return type.IsNullable;
}

return true;
}

private ValueExpression? GetPropertyInitializationValue(InputModelProperty property, CSharpType propertyType)
{
if (!property.IsRequired)
return null;

if (propertyType.IsLiteral)
{
if (!propertyType.IsNullable)
{
return Literal(propertyType.Literal);
}
else
{
return DefaultOf(propertyType);
}
}

return null;
}

protected override CSharpMethod[] BuildConstructors()
{
List<CSharpMethod> constructors = new List<CSharpMethod>();
Expand Down Expand Up @@ -164,31 +91,26 @@ private IReadOnlyList<Parameter> BuildConstructorParameters(bool isSerialization
foreach (var property in _inputModel.Properties)
{
CSharpType propertyType = CodeModelPlugin.Instance.TypeFactory.CreateCSharpType(property.Type);
var initializationValue = GetPropertyInitializationValue(property, propertyType);
var parameterValidation = GetParameterValidation(property, propertyType);

var parameter = new Parameter(
Name: property.Name.ToVariableName(),
Description: FormattableStringHelpers.FromString(property.Description),
Type: propertyType,
DefaultValue: null,
Validation: parameterValidation,
Initializer: null);

// All properties should be included in the serialization ctor
if (isSerializationConstructor)
{
constructorParameters.Add(parameter with { Validation = ValidationType.None });
constructorParameters.Add(new Parameter(property)
{
Validation = ValidationType.None,
jorgerangel-msft marked this conversation as resolved.
Show resolved Hide resolved
});
}
else
{
// For classes, only required + not readonly + not initialization value + not discriminator could get into the public ctor
// For structs, all properties must be set in the public ctor
if (IsStruct || (property is { IsRequired: true, IsDiscriminator: false } && initializationValue == null))
if (IsStruct || (property is { IsRequired: true, IsDiscriminator: false } && !propertyType.IsLiteral))
{
if (!property.IsReadOnly)
{
constructorParameters.Add(parameter with { Type = parameter.Type.InputType });
constructorParameters.Add(new Parameter(property)
{
Type = propertyType.InputType,
jorgerangel-msft marked this conversation as resolved.
Show resolved Hide resolved
});
}
}
}
Expand All @@ -197,35 +119,6 @@ private IReadOnlyList<Parameter> BuildConstructorParameters(bool isSerialization
return constructorParameters;
}

private static ValidationType GetParameterValidation(InputModelProperty property, CSharpType propertyType)
{
// We do not validate a parameter when it is a value type (struct or int, etc)
if (propertyType.IsValueType)
{
return ValidationType.None;
}

// or it is readonly
if (property.IsReadOnly)
{
return ValidationType.None;
}

// or it is optional
if (!property.IsRequired)
{
return ValidationType.None;
}

// or it is nullable
if (propertyType.IsNullable)
{
return ValidationType.None;
}

return ValidationType.AssertNotNull;
}

private CSharpMethod? BuildInitializationConstructor()
{
if (_inputModel.IsUnknownDiscriminatorModel)
Expand Down
Loading
Loading