in src/AutoRest.CSharp/Common/Output/Models/Shared/Parameter.cs [21:253]
internal record Parameter(string Name, FormattableString? Description, CSharpType Type, Constant? DefaultValue, ValidationType Validation, FormattableString? Initializer, bool IsApiVersionParameter = false, bool IsEndpoint = false, bool IsResourceIdentifier = false, bool SkipUrlEncoding = false, RequestLocation RequestLocation = RequestLocation.None, SerializationFormat SerializationFormat = SerializationFormat.Default, bool IsPropertyBag = false, bool IsRef = false, bool IsOut = false)
{
public bool IsRawData { get; init; }
public static IEqualityComparer<Parameter> TypeAndNameEqualityComparer = new ParameterTypeAndNameEqualityComparer();
public CSharpAttribute[] Attributes { get; init; } = Array.Empty<CSharpAttribute>();
public bool IsOptionalInSignature => DefaultValue != null;
public Parameter WithRef(bool isRef = true) => IsRef == isRef ? this : this with { IsRef = isRef };
public Parameter ToRequired()
{
return this with { DefaultValue = null };
}
public static Parameter FromInputParameter(in InputParameter operationParameter, CSharpType type, TypeFactory typeFactory, bool shouldKeepClientDefaultValue = false)
{
var name = ConstructParameterVariableName(operationParameter, type);
var skipUrlEncoding = operationParameter.SkipUrlEncoding;
var requestLocation = operationParameter.Location;
bool keepClientDefaultValue = shouldKeepClientDefaultValue || operationParameter.Kind == InputOperationParameterKind.Constant || operationParameter.IsApiVersion || operationParameter.IsContentType || operationParameter.IsEndpoint;
Constant? clientDefaultValue = GetDefaultValue(operationParameter, typeFactory);
var defaultValue = keepClientDefaultValue
? clientDefaultValue
: (Constant?)null;
var initializer = (FormattableString?)null;
if (defaultValue != null && operationParameter.Kind != InputOperationParameterKind.Constant && !type.CanBeInitializedInline(defaultValue))
{
initializer = type.GetParameterInitializer(defaultValue.Value);
type = type.WithNullable(true);
defaultValue = Constant.Default(type);
}
if (!operationParameter.IsRequired && defaultValue == null)
{
type = type.WithNullable(true);
defaultValue = Constant.Default(type);
}
var validation = operationParameter.IsRequired && initializer == null
? GetValidation(type, requestLocation, skipUrlEncoding)
: ValidationType.None;
var inputType = type.InputType;
return new Parameter(
name,
CreateDescription(operationParameter, inputType, (operationParameter.Type.GetImplementType() as InputEnumType)?.Values.Select(c => c.GetValueString()), keepClientDefaultValue ? null : clientDefaultValue),
inputType,
defaultValue,
validation,
initializer,
IsApiVersionParameter: operationParameter.IsApiVersion,
IsEndpoint: operationParameter.IsEndpoint,
IsResourceIdentifier: operationParameter.IsResourceParameter,
SkipUrlEncoding: skipUrlEncoding,
RequestLocation: requestLocation,
SerializationFormat: SerializationBuilder.GetSerializationFormat(operationParameter.Type));
}
private static Constant? GetDefaultValue(InputParameter operationParameter, TypeFactory typeFactory) => operationParameter switch
{
{ NameInRequest: var nameInRequest } when RequestHeader.ClientRequestIdHeaders.Contains(nameInRequest) => Constant.FromExpression($"message.{Configuration.ApiTypes.HttpMessageRequestName}.ClientRequestId", new CSharpType(typeof(string))),
{ NameInRequest: var nameInRequest } when RequestHeader.ReturnClientRequestIdResponseHeaders.Contains(nameInRequest) => new Constant("true", new CSharpType(typeof(string))),
{ DefaultValue: not null } => BuilderHelpers.ParseConstant(operationParameter.DefaultValue.Value, typeFactory.CreateType(operationParameter.DefaultValue.Type)),
{ Type: InputLiteralType { Value: not null} literalValue } => BuilderHelpers.ParseConstant(literalValue.Value, typeFactory.CreateType(literalValue.ValueType)),
{ NameInRequest: var nameInRequest } when nameInRequest.Equals(RequestHeader.RepeatabilityRequestId, StringComparison.OrdinalIgnoreCase) =>
// Guid.NewGuid()
Constant.FromExpression($"{nameof(Guid)}.{nameof(Guid.NewGuid)}()", new CSharpType(typeof(string))),
{ NameInRequest: var nameInRequest } when nameInRequest.Equals(RequestHeader.RepeatabilityFirstSent, StringComparison.OrdinalIgnoreCase) =>
// DateTimeOffset.Now
Constant.FromExpression($"{nameof(DateTimeOffset)}.{nameof(DateTimeOffset.Now)}", new CSharpType(typeof(DateTimeOffset))),
_ => (Constant?)null,
};
public static FormattableString CreateDescription(InputParameter operationParameter, CSharpType type, IEnumerable<string>? values, Constant? defaultValue = null)
{
FormattableString description = GetDescription(operationParameter, type);
if (defaultValue != null)
{
var defaultValueString = defaultValue?.Value is string s ? $"\"{s}\"" : $"{defaultValue?.Value}";
description = $"{description}{(description.ToString().EndsWith(".") ? "" : ".")} The default value is {defaultValueString}";
}
if (!type.IsFrameworkType || values == null)
{
return description;
}
var allowedValues = string.Join(" | ", values.Select(v => $"\"{v}\""));
return $"{description}{(description.ToString().EndsWith(".") ? "" : ".")} Allowed values: {BuilderHelpers.EscapeXmlDocDescription(allowedValues)}";
}
/// <summary>
/// This method constructs the variable name for an input parameter. If the input parameter type is an input model type,
/// and the input parameter name is the same as the input parameter type name, the variable name is constructed using the supplied CSharpType name. Otherwise,
/// it will use the input parameter name by default.
/// </summary>
/// <param name="param">The input parameter.</param>
/// <param name="type">The constructed CSharpType for the input parameter.</param>
/// <returns>A string representing the variable name for the input parameter.</returns>
private static string ConstructParameterVariableName(InputParameter param, CSharpType type)
{
string paramName = param.Name;
string variableName = paramName.ToVariableName();
if (param.Type.GetImplementType() is InputModelType paramInputType)
{
var paramInputTypeName = paramInputType.Name;
if (paramName.Equals(paramInputTypeName) || // remove this after adoption of TCGC
paramName.Equals(paramInputTypeName.ToVariableName()))
{
variableName = !string.IsNullOrEmpty(type.Name) ? type.Name.ToVariableName() : variableName;
}
}
return variableName;
}
public static ValidationType GetValidation(CSharpType type, RequestLocation requestLocation, bool skipUrlEncoding)
{
if (requestLocation is RequestLocation.Uri or RequestLocation.Path or RequestLocation.Body && type.EqualsIgnoreNullable(typeof(string)) && !skipUrlEncoding)
{
return ValidationType.AssertNotNullOrEmpty;
}
if (!type.IsValueType)
{
return ValidationType.AssertNotNull;
}
return ValidationType.None;
}
private static FormattableString CreateDescription(InputParameter requestParameter, CSharpType type, Constant? defaultValue = null)
{
FormattableString description = GetDescription(requestParameter, type);
if (defaultValue != null)
{
var defaultValueString = defaultValue?.Value is string s ? $"\"{s}\"" : $"{defaultValue?.Value}";
description = $"{description}{(description.ToString().EndsWith(".") ? "" : ".")} The default value is {defaultValueString}";
}
return requestParameter.Type switch
{
InputEnumType choiceSchema when type.IsFrameworkType => AddAllowedValues(description, choiceSchema.Values),
InputNullableType { Type: InputEnumType ie } => AddAllowedValues(description, ie.Values),
_ => description
};
static FormattableString AddAllowedValues(FormattableString description, IReadOnlyList<InputEnumTypeValue> choices)
{
var allowedValues = choices.Select(c => (FormattableString)$"{c.Value:L}").ToArray().Join(" | ");
return allowedValues.IsNullOrEmpty()
? description
: $"{description}{(description.ToString().EndsWith(".") ? "" : ".")} Allowed values: {BuilderHelpers.EscapeXmlDocDescription(allowedValues.ToString())}";
}
}
private static FormattableString GetDescription(InputParameter parameter, CSharpType type)
{
var description = DocHelpers.GetDescription(parameter.Summary, parameter.Doc);
if (description is null)
{
return $"The {type:C} to use.";
}
return FormattableStringHelpers.FromString(BuilderHelpers.EscapeXmlDocDescription(description));
}
private static Constant? ParseConstant(InputParameter parameter, TypeFactory typeFactory)
{
if (parameter.Location == RequestLocation.Header)
{
if (RequestHeader.ClientRequestIdHeaders.Contains(parameter.NameInRequest ?? parameter.Name))
{
return Constant.FromExpression($"message.{Configuration.ApiTypes.HttpMessageRequestName}.ClientRequestId", new CSharpType(typeof(string)));
}
else if (RequestHeader.ReturnClientRequestIdResponseHeaders.Contains(parameter.NameInRequest ?? parameter.Name))
{
return new Constant("true", new CSharpType(typeof(string)));
}
}
if (parameter.Kind == InputOperationParameterKind.Constant && parameter.IsRequired)
{
return GetDefaultValue(parameter, typeFactory);
}
return null;
}
public static readonly IEqualityComparer<Parameter> EqualityComparerByType = new ParameterByTypeEqualityComparer();
private struct ParameterByTypeEqualityComparer : IEqualityComparer<Parameter>
{
public bool Equals(Parameter? x, Parameter? y)
{
return object.Equals(x?.Type, y?.Type);
}
public int GetHashCode([DisallowNull] Parameter obj) => obj.Type.GetHashCode();
}
private class ParameterTypeAndNameEqualityComparer : IEqualityComparer<Parameter>
{
public bool Equals(Parameter? x, Parameter? y)
{
if (object.ReferenceEquals(x, y))
{
return true;
}
if (x is null || y is null)
{
return false;
}
// We can't use CsharpType.Equals here because they can have different implementations from different versions
var result = x.Type.AreNamesEqual(y.Type) && x.Name == y.Name;
return result;
}
public int GetHashCode([DisallowNull] Parameter obj)
{
// remove type as part of the hash code generation as the type might have changes between versions
return HashCode.Combine(obj.Name);
}
}
}