@azure-tools/typespec-java
Version:
TypeSpec library for emitting Java client from the TypeSpec REST protocol binding
326 lines • 11.4 kB
JavaScript
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