UNPKG

@azure-tools/typespec-java

Version:

TypeSpec library for emitting Java client from the TypeSpec REST protocol binding

326 lines 11.4 kB
import { getUnionAsEnum } from "@azure-tools/typespec-azure-core"; import { isSdkFloatKind, isSdkIntKind, } from "@azure-tools/typespec-client-generator-core"; import { getTypeName, isNullType, isTemplateDeclaration, isTemplateInstance, isTypeSpecValueTypeOf, } from "@typespec/compiler"; import { SchemaContext } from "./common/schemas/usage.js"; import { getNamespace } from "./utils.js"; export const DURATION_KNOWN_ENCODING = ["ISO8601", "seconds"]; export const DATETIME_KNOWN_ENCODING = ["rfc3339", "rfc7231", "unixTimestamp"]; export const BYTES_KNOWN_ENCODING = ["base64", "base64url"]; /** Acts as a cache for processing inputs. * * If the input is undefined, the output is always undefined. * for a given input, the process is only ever called once. */ export class ProcessingCache { constructor(transform) { this.transform = transform; this.results = new Map(); } has(original) { return !!original && !!this.results.get(original); } set(original, result) { this.results.set(original, result); return result; } get(original) { return this.results.get(original); } process(original, ...args) { if (original) { const result = this.results.get(original) || this.transform(original, ...args); this.results.set(original, result); return result; } return undefined; } } /** adds only if the item is not in the collection already * * @note While this isn't very efficient, it doesn't disturb the original * collection, so you won't get inadvertent side effects from using Set, etc. */ export function pushDistinct(targetArray, ...items) { for (const i of items) { if (!targetArray.includes(i)) { targetArray.push(i); } } return targetArray; } export function modelContainsDerivedModel(model) { return (!isTemplateDeclaration(model) && !(isTemplateInstance(model) && model.derivedModels.length === 0)); } export function isModelReferredInTemplate(template, target) { var _a, _b, _c; return (template === target || ((_c = (_b = (_a = template === null || template === void 0 ? void 0 : template.templateMapper) === null || _a === void 0 ? void 0 : _a.args) === null || _b === void 0 ? void 0 : _b.some((it) => "kind" in it && (it.kind === "Model" || it.kind === "Union") ? isModelReferredInTemplate(it, target) : false)) !== null && _c !== void 0 ? _c : false)); } export function isNullableType(type) { if (type.kind === "Union") { const nullVariants = Array.from(type.variants.values()).filter((it) => isNullType(it.type)); return nullVariants.length >= 1; } else { return false; } } export function getNonNullSdkType(type) { return type.kind === "nullable" ? type.type : type; } export function getDefaultValue(value) { if (value) { switch (value.valueKind) { case "StringValue": return value.value; case "NumericValue": return value.value; case "BooleanValue": return value.value; } } return undefined; } export function getDurationFormat(type) { let format = "duration-rfc3339"; // duration encoded as seconds if (type.encode === "seconds") { if (isSdkIntKind(type.wireType.kind)) { format = "seconds-integer"; } else if (isSdkFloatKind(type.wireType.kind)) { format = "seconds-number"; } else { throw new Error(`Unrecognized scalar type used by duration encoded as seconds: '${type.kind}'.`); } } return format; } export function hasScalarAsBase(type, scalarName) { let scalarType = type; while (scalarType) { if (scalarType.name === scalarName) { return true; } scalarType = scalarType.baseScalar; } return false; } export function unionReferredByType(program, type, cache) { if (cache.has(type)) { const ret = cache.get(type); if (ret) { return ret; } else { return null; } } cache.set(type, undefined); if (type.kind === "Union") { // ref CodeModelBuilder.processUnionSchema const nonNullVariants = Array.from(type.variants.values()).filter((it) => !isNullType(it.type)); if (nonNullVariants.length === 1) { // Type | null, follow that Type const ret = unionReferredByType(program, nonNullVariants[0], cache); if (ret) { cache.set(type, ret); return ret; } } else if (getUnionAsEnum(type)) { // "literal1" | "literal2" -> Enum cache.set(type, null); return null; } else { // found Union cache.set(type, type); return type; } } else if (type.kind === "Model") { if (type.indexer) { // follow indexer (for Array/Record) const ret = unionReferredByType(program, type.indexer.value, cache); if (ret) { cache.set(type, ret); return ret; } } // follow properties for (const property of type.properties.values()) { const ret = unionReferredByType(program, property.type, cache); if (ret) { cache.set(type, ret); return ret; } } cache.set(type, null); return null; } cache.set(type, null); return null; } export function getUnionDescription(union, typeNameOptions) { let name = union.name; if (!name) { const names = []; union.variants.forEach((it) => { names.push(getTypeName(it.type, typeNameOptions)); }); name = names.join(" | "); } return name; } export function modelIs(model, name, namespace) { // use raw model because SdkModelType does not have sourceModel information let currentModel = model.__raw; while (currentModel) { if (currentModel.name === name && getNamespace(currentModel) === namespace) { return true; } currentModel = currentModel.sourceModel; } return false; } export function getAccess(type, accessCache) { if (type && (type.kind === "Model" || type.kind === "Operation" || type.kind === "Enum" || type.kind === "Union" || type.kind === "Namespace")) { let access = getDecoratorScopedValue(type, "$access", (it) => { const value = it.args[0].value; if ("kind" in value && value.kind === "EnumMember") { return value.name; } else { return undefined; } }); if (!access && type.namespace) { // check (parent) namespace if (accessCache.has(type.namespace)) { access = accessCache.get(type.namespace); } else { access = getAccess(type.namespace, accessCache); accessCache.set(type.namespace, access); } } return access; } else { return undefined; } } export function isAllValueInteger(values) { return values.every((it) => Number.isInteger(it)); } export function getUsage(type, usageCache) { if (type && (type.kind === "Model" || type.kind === "Operation" || type.kind === "Enum" || type.kind === "Union" || type.kind === "Namespace")) { let usage = getDecoratorScopedValue(type, "$usage", (it) => { const value = it.args[0].value; const values = []; const ret = []; if ("kind" in value && value.kind === "EnumMember") { values.push(value); } else if ("kind" in value && value.kind === "Union") { for (const v of value.variants.values()) { values.push(v.type); } } else { return undefined; } for (const v of values) { switch (v.name) { case "input": ret.push(SchemaContext.Input); break; case "output": ret.push(SchemaContext.Output); break; } } if (ret.length === 0) { return undefined; } return ret; }); if (!usage && type.namespace) { // check (parent) namespace if (usageCache.has(type.namespace)) { usage = usageCache.get(type.namespace); } else { usage = getUsage(type.namespace, usageCache); usageCache.set(type.namespace, usage); } } return usage; } else { return undefined; } } /** * Check if a given model or model property is an ARM common type. * This is copied from typespec-azure-resource-manager. We don't want to depend on this package since it now has weird dependency on typespec-autorest. * * @param {Type} entity - The entity to be checked. * @return {boolean} - A boolean value indicating whether an entity is an ARM common type. */ export function isArmCommonType(entity) { const commonDecorators = ["$armCommonDefinition", "$armCommonParameter"]; if (isTypeSpecValueTypeOf(entity, ["Model", "ModelProperty"])) { return commonDecorators.some((commonDecorator) => entity.decorators.some((d) => d.decorator.name === commonDecorator)); } return false; } export function getPropertySerializedName(property) { var _a, _b, _c, _d, _e, _f; // still fallback to "property.name", as for orphan model, serializationOptions.json is undefined return ((_f = (_d = (_b = (_a = property.serializationOptions.json) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : (_c = property.serializationOptions.multipart) === null || _c === void 0 ? void 0 : _c.name) !== null && _d !== void 0 ? _d : (_e = property.__raw) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : property.name); } function getDecoratorScopedValue(type, decorator, mapFunc) { let value = type.decorators .filter((it) => it.decorator.name === decorator && it.args.length === 2 && it.args[1].value.value === "java") .map((it) => mapFunc(it)) .find(() => true); if (value) { return value; } value = type.decorators .filter((it) => it.decorator.name === decorator && it.args.length === 2 && it.args[1].value.value === "client") .map((it) => mapFunc(it)) .find(() => true); if (value) { return value; } value = type.decorators .filter((it) => it.decorator.name === decorator && it.args.length === 1) .map((it) => mapFunc(it)) .find(() => true); if (value) { return value; } return undefined; } //# sourceMappingURL=type-utils.js.map