powershell/llcsharp/schema/fixed-array.ts (213 lines of code) (raw):
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KnownMediaType } from '@azure-tools/codemodel-v3';
import { camelCase, deconstruct, nameof } from '@azure-tools/codegen';
import { IsNotNull } from '@azure-tools/codegen-csharp';
import { dotnet, System } from '@azure-tools/codegen-csharp';
import { Expression, ExpressionOrLiteral, toExpression, valueOf } from '@azure-tools/codegen-csharp';
import { ForEach } from '@azure-tools/codegen-csharp';
import { If } from '@azure-tools/codegen-csharp';
import { OneOrMoreStatements } from '@azure-tools/codegen-csharp';
import { Ternery } from '@azure-tools/codegen-csharp';
import { LocalVariable, Variable } from '@azure-tools/codegen-csharp';
import { ClientRuntime } from '../clientruntime';
import { Schema } from '../code-model';
import { Schema as NewSchema } from '@autorest/codemodel';
import { popTempVar, pushTempVar } from '../schema/primitive';
import { EnhancedTypeDeclaration } from './extended-type-declaration';
export class FixedArrayOf implements EnhancedTypeDeclaration {
public isXmlAttribute = false;
public isNullable = true;
get defaultOfType() {
return toExpression('null /* fixedArrayOf */');
}
constructor(public schema: NewSchema, public isRequired: boolean, public elementType: EnhancedTypeDeclaration, protected minItems: number | undefined, protected maxItems: number | undefined, protected unique: boolean | undefined) {
}
protected get isWrapped(): boolean {
return this.schema.serialization?.xml && this.schema.serialization?.xml.wrapped || false;
}
protected get wrapperName(): string | undefined {
return this.schema.serialization?.xml && this.isWrapped ? this.schema.serialization.xml.name : undefined;
}
protected get serializedName(): string | undefined {
return this.schema.serialization?.xml ? this.schema.serialization.xml.name : undefined;
}
get elementTypeDeclaration(): string {
return this.elementType.declaration;
}
get declaration(): string {
return `${this.elementType.declaration}[]`;
}
get encode(): string {
this.schema.extensions = this.schema.extensions || {};
return this.schema.extensions['x-ms-skip-url-encoding'] ? '' : 'global::System.Uri.EscapeDataString';
}
get convertObjectMethod() {
try {
const v = pushTempVar();
const i = pushTempVar();
// return `${v} => ${v} is string || !(${v} is global::System.Collections.IEnumerable) ? new ${this.declaration} { ${this.elementType.convertObjectMethod}(${v}) } : System.Linq.Enumerable.ToArray( System.Linq.Enumerable.Select( System.Linq.Enumerable.OfType<object>((global::System.Collections.IEnumerable)${v}), ${this.elementType.convertObjectMethod}))`
return `${v} => TypeConverterExtensions.SelectToArray<${this.elementTypeDeclaration}>(${v}, ${this.elementType.convertObjectMethod})`;
} finally {
popTempVar();
popTempVar();
}
}
/** emits an expression to deserialize a property from a member inside a container */
deserializeFromContainerMember(mediaType: KnownMediaType, container: ExpressionOrLiteral, serializedName: string, defaultValue: Expression): Expression {
switch (mediaType) {
case KnownMediaType.Json: {
// json array
const tmp = `__${camelCase(['json', ...deconstruct(serializedName)])}`;
return toExpression(`If( ${valueOf(container)}?.PropertyT<${ClientRuntime.JsonArray}>("${serializedName}"), out var ${tmp}) ? ${this.deserializeFromNode(mediaType, tmp, toExpression('null'))} : ${defaultValue}`);
}
case KnownMediaType.Xml: {
// XElement/XElement
const tmp = `__${camelCase(['xml', ...deconstruct(serializedName)])}`;
if (this.isWrapped) {
// wrapped xml arrays will have a container around them.
return toExpression(`${this.deserializeFromNode(mediaType, `${valueOf(container)}?.Element("${this.serializedName || serializedName}")`, defaultValue)}`);
} else {
// whereas non-wrapped will have all the elements in the container directly.
return toExpression(`${this.deserializeFromNode(mediaType, `${valueOf(container)}`, defaultValue)}`);
}
}
}
return toExpression(`null /* deserializeFromContainerMember doesn't support '${mediaType}' ${__filename}*/`);
}
/** emits an expression to deserialze a container as the value itself. */
deserializeFromNode(mediaType: KnownMediaType, node: ExpressionOrLiteral, defaultValue: Expression): Expression {
try {
const tmp = pushTempVar();
const each = pushTempVar();
switch (mediaType) {
case KnownMediaType.Json: {
// const deser = `System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select( ${tmp} , (${each})=>(${this.elementType.declaration}) (${this.elementType.deserializeFromNode(mediaType, each, this.elementType.defaultOfType)}) ) )`;
const deser = System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select(tmp, `(${each})=>(${this.elementType.declaration}) (${this.elementType.deserializeFromNode(mediaType, each, this.elementType.defaultOfType)}`));
return toExpression(`If( ${valueOf(node)} as ${ClientRuntime.JsonArray}, out var ${tmp}) ? ${System.Func(this).new(`()=> ${valueOf(deser)} )`)}() : ${defaultValue}`);
}
case KnownMediaType.Xml: {
// XElement should be a container of items, right?
// if the reference doesn't define an XML schema then use its default name
//const defaultName = this.elementType.schema.details.csharp.name;
//const deser = `System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select( ${tmp}.Elements("${this.elementType.schema.xml ? this.elementType.schema.xml.name || defaultName : defaultName}"), (${each})=> ${this.elementType.deserializeFromNode(mediaType, each, toExpression('null'))} ) )`;
//return toExpression(`If( ${valueOf(node)}, out var ${tmp}) ? new System.Func<${this.elementType.declaration}[]>(()=> ${deser} )() : ${defaultValue}`);
}
}
} finally {
popTempVar();
popTempVar();
}
return toExpression(`null /* deserializeFromNode doesn't support '${mediaType}' ${__filename}*/`);
}
/** emits an expression to deserialize content from a string */
deserializeFromString(mediaType: KnownMediaType, content: ExpressionOrLiteral, defaultValue: Expression): Expression | undefined {
switch (mediaType) {
case KnownMediaType.Json: {
return this.deserializeFromNode(mediaType, ClientRuntime.JsonArray.Parse(content), defaultValue);
}
case KnownMediaType.Xml: {
return this.deserializeFromNode(mediaType, `${System.Xml.Linq.XElement}.Parse(${content})`, defaultValue);
}
}
return undefined;
}
/** emits an expression to deserialize content from a content/response */
deserializeFromResponse(mediaType: KnownMediaType, content: ExpressionOrLiteral, defaultValue: Expression): Expression | undefined {
switch (mediaType) {
case KnownMediaType.Xml:
case KnownMediaType.Json: {
return toExpression(`${content}.Content.ReadAsStringAsync().ContinueWith( body => ${this.deserializeFromString(mediaType, 'body.Result', defaultValue)})`);
}
}
return toExpression(`null /* deserializeFromResponse doesn't support '${mediaType}' ${__filename}*/`);
}
/** emits an expression serialize this to a HttpContent */
serializeToNode(mediaType: KnownMediaType, value: ExpressionOrLiteral, serializedName: string, mode: Expression): Expression {
try {
const each = pushTempVar();
switch (mediaType) {
case KnownMediaType.Json: {
const serArray = `global::System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select(${value}, (${each}) => ${this.elementType.serializeToNode(mediaType, each, serializedName, mode)}))`;
return toExpression(`null != ${value} ? new ${ClientRuntime.XNodeArray}(${serArray}) : null`);
}
case KnownMediaType.Xml: {
if (this.isWrapped) {
const name = this.elementType.schema.serialization?.xml ? this.elementType.schema.serialization?.xml.name || serializedName : serializedName;
return toExpression(`null != ${value} ? global::new System.Xml.Linq.XElement("${name}", global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(${value}, (${each}) => ${this.elementType.serializeToNode(mediaType, each, name, mode)}))`);
} else {
throw new Error('Can\'t set an Xml Array to the document without wrapping it.');
}
}
case KnownMediaType.Cookie:
case KnownMediaType.QueryParameter: {
const formatSerializedName = serializedName ? `${serializedName}=` : '';
return toExpression(`(null != ${value} && ${value}.Length > 0 ? "${formatSerializedName}" + ${this.encode}(global::System.Linq.Enumerable.Aggregate(${value}, (current, each) => current + "," + ( null == each ? ${System.String.Empty} : each.ToString()) )) : ${System.String.Empty})`);
}
case KnownMediaType.Header:
case KnownMediaType.Text:
case KnownMediaType.UriParameter:
return toExpression(`(null != ${value} ? ${this.encode}(global::System.Linq.Enumerable.Aggregate(${value}, (current,each)=> current + "," + ${this.elementType.serializeToNode(mediaType, 'each', '', mode)})) : ${System.String.Empty})`);
}
} finally {
popTempVar();
}
return toExpression(`null /* serializeToNode doesn't support '${mediaType}' ${__filename}*/`);
}
/** emits an expression serialize this to the value required by the container */
serializeToContent(mediaType: KnownMediaType, value: ExpressionOrLiteral, mode: Expression): Expression {
try {
const each = pushTempVar();
switch (mediaType) {
case KnownMediaType.Json: {
return System.Net.Http.StringContent.new(
`${(this.serializeToNode(mediaType, value, '', mode))}`,
System.Text.Encoding.UTF8);
}
case KnownMediaType.Xml: {
// if the reference doesn't define an XML schema then use its default name
const defaultName = this.elementType.schema.language.csharp?.name || '';
return System.Net.Http.StringContent.new(Ternery(
IsNotNull(value),
`${this.serializeToNode(mediaType, value, this.schema.serialization?.xml ? this.schema.serialization.xml?.name || defaultName : defaultName, mode)}).ToString()`,
System.String.Empty
), System.Text.Encoding.UTF8);
}
case KnownMediaType.Cookie:
case KnownMediaType.QueryParameter:
case KnownMediaType.Header:
case KnownMediaType.Text:
case KnownMediaType.UriParameter:
return toExpression(`(null != ${value} ? ${this.encode}(System.Linq.Enumerable.Aggregate(${value}, (current,each)=> current + "," + ${this.elementType.serializeToNode(mediaType, 'each', '', mode)})) : ${System.String.Empty})`);
}
} finally {
popTempVar();
}
return toExpression(`null /* serializeToContent doesn't support '${mediaType}' ${__filename}*/`);
}
/** emits the code required to serialize this into a container */
serializeToContainerMember(mediaType: KnownMediaType, value: ExpressionOrLiteral, container: Variable, serializedName: string, mode: Expression): OneOrMoreStatements {
try {
const each = pushTempVar();
const tmp = pushTempVar();
switch (mediaType) {
case KnownMediaType.Json: {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const $this = this;
return If(`null != ${value}`, function* () {
const t = new LocalVariable(tmp, dotnet.Var, { initializer: `new ${ClientRuntime.XNodeArray}()` });
yield t.declarationStatement;
yield ForEach(each, toExpression(value), `AddIf(${$this.elementType.serializeToNode(mediaType, each, '', mode)} ,${tmp}.Add);`);
yield `${container}.Add("${serializedName}",${tmp});`;
});
}
case KnownMediaType.Xml:
if (this.isWrapped) {
return `AddIf( ${System.Xml.Linq.XElement.new('"{this.serializedName || serializedName}"', `${this.serializeToNode(mediaType, value, this.elementType.schema.serialization?.xml ? this.elementType.schema.serialization?.xml.name || '!!!' : serializedName, mode)}):null`)}, ${container}.Add); `;
} else {
return If(`null != ${value}`, ForEach(each, toExpression(value), `AddIf(${this.elementType.serializeToNode(mediaType, each, serializedName, mode)}, ${container}.Add);`));
}
}
} finally {
popTempVar();
popTempVar();
}
return (`/* serializeToContainerMember doesn't support '${mediaType}' ${__filename}*/`);
}
public validatePresence(eventListener: Variable, property: Variable): OneOrMoreStatements {
if (this.isRequired) {
return `await ${eventListener}.AssertNotNull(${nameof(property.value)}, ${property}); `;
}
return '';
}
validateValue(eventListener: Variable, property: Variable): OneOrMoreStatements {
// check if the underlyingType has validation.
if (!this.elementType.validateValue(eventListener, new LocalVariable(`${property} [{ __i }]`, dotnet.Var))) {
return '';
}
return `
if (${property} != null ) {
for (int __i = 0; __i < ${property}.Length; __i++) {
${this.elementType.validateValue(eventListener, new LocalVariable(`${property}[__i]`, dotnet.Var))}
}
}
`.trim();
}
}